• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *   Copyright (c) International Business Machines  Corp., 2002
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 /* 06/30/2001	Port to Linux	nsharoff@us.ibm.com */
21 /* 10/30/2002	Port to LTP	dbarrera@us.ibm.com */
22 
23 /*
24  * NAME
25  *	mkdir.c - Stress test of mkdir call.
26  *
27  * CALLS
28  *	mkdir, rmdir
29  *
30  * ALGORITHM
31  *	Create multiple processes which create subdirectories in the
32  *	same directory multiple times. On exit of all child processes,
33  *	make sure all subdirectories can be removed.
34  *
35  *      USAGE: mkdir09 -c # -t # -d #
36  *              -c = number of children groups
37  *              -t = number of seconds to run test
38  *              -d = number of directories created in test directory
39  *
40  * RESTRICTIONS
41  *
42  */
43 
44 #include <stdio.h>		/* needed by testhead.h         */
45 #include <wait.h>		/* needed by testhead.h         */
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/mman.h>
50 #include <errno.h>
51 #include <signal.h>
52 #include <unistd.h>
53 #include <setjmp.h>
54 #include "test.h"
55 
56 #include <stdlib.h>
57 #include <stdlib.h>
58 #include <string.h>
59 
60 #define NCHILD		3
61 
62 #define MODE_RWX	07770
63 #define DIR_NAME	"./X.%d"
64 
65 /* used by getopt */
66 extern char *optarg;
67 extern int optind, opterr;
68 char *goodopts = "c:t:d:";
69 int errflg;
70 
71 char *TCID = "mkdir09";
72 int TST_TOTAL = 1;
73 
74 int child_groups, test_time, nfiles;
75 char testdir[MAXPATHLEN];
76 int parent_pid, sigchld, sigterm, jump;
77 void term(int sig);
78 void chld(int sig);
79 int *pidlist, child_count;
80 jmp_buf env_buf;
81 
82 int getchild(int group, int child, int children);
83 int dochild1(void);
84 int dochild2(void);
85 int dochild3(int group);
86 int massmurder(void);
87 int runtest(void);
88 void setup(void);
89 void cleanup(void);
90 
91 #ifdef UCLINUX
92 static char *argv0;
93 void dochild1_uclinux(void);
94 void dochild2_uclinux(void);
95 void dochild3_uclinux(void);
96 static int group_uclinux;
97 #endif
98 
99 /*--------------------------------------------------------------*/
100 /*--------------------------------------------------------------*/
101 /*--------------------------------------------------------------*/
main(int argc,char * argv[])102 int main(int argc, char *argv[])
103 {
104 	int c;
105 
106 #ifdef UCLINUX
107 
108 	tst_parse_opts(argc, argv, NULL, NULL);
109 
110 	argv0 = argv[0];
111 	maybe_run_child(&dochild1_uclinux, "nd", 1, &nfiles);
112 	maybe_run_child(&dochild2_uclinux, "n", 2);
113 	maybe_run_child(&dochild3_uclinux, "nd", 3, &group_uclinux);
114 #endif
115 
116 	setup();
117 
118 	/* Set up to catch SIGTERM signal */
119 	if (signal(SIGTERM, term) == SIG_ERR) {
120 		tst_brkm(TFAIL, cleanup,
121 			 "Error setting up SIGTERM signal, ERRNO = %d", errno);
122 
123 	}
124 
125 	/* Set up to catch SIGCLD signal */
126 	if (signal(SIGCLD, chld) == SIG_ERR) {
127 		tst_brkm(TFAIL, cleanup,
128 			 "Error setting up SIGCLD signal, ERRNO = %d", errno);
129 
130 	}
131 
132 	/* Default argument settings. */
133 
134 	child_groups = 2;
135 	test_time = 5;		/* 0 = run forever or till signal */
136 	nfiles = 5;
137 
138 	/* Get command line options */
139 
140 	while ((c = getopt(argc, argv, goodopts)) != EOF) {
141 		switch (c) {
142 		case 'c':
143 			child_groups = atoi(optarg);
144 			break;
145 		case 't':
146 			test_time = atoi(optarg);
147 			break;
148 		case 'd':
149 			nfiles = atoi(optarg);
150 			break;
151 		case '?':
152 			errflg++;
153 			break;
154 		default:
155 			break;
156 		}
157 	}
158 	if (errflg) {
159 		tst_resm(TINFO,
160 			 "USAGE : mkdir09 -c #child_groups -t#test_time -d#directories");
161 		tst_resm(TINFO, "Bad argument count.");
162 
163 	}
164 
165 	runtest();
166 	cleanup();
167 	tst_exit();
168 
169 }
170 
171 /*--------------------------------------------------------------*/
172 
runtest(void)173 int runtest(void)
174 {
175 	int i, j;
176 	int count, child, status;
177 	char tmpdir[MAXPATHLEN];
178 
179 	/* Create permanent directories with holes in directory structure */
180 
181 	for (j = 0; j < nfiles; j++) {
182 		sprintf(tmpdir, DIR_NAME, j);
183 		TEST(mkdir(tmpdir, MODE_RWX));
184 
185 		if (TEST_RETURN < 0) {
186 			tst_brkm(TFAIL, cleanup,
187 				 "Error creating permanent directories, ERRNO = %d",
188 				 TEST_ERRNO);
189 		}
190 		if ((j % NCHILD) != 0) {
191 			if (rmdir(tmpdir) < 0) {
192 				tst_brkm(TFAIL, cleanup,
193 					 "Error removing directory, ERRNO = %d",
194 					 errno);
195 			}
196 		}
197 	}
198 
199 	parent_pid = getpid();
200 
201 	/* allocate space for list of child pid's */
202 
203 	if ((pidlist = malloc((child_groups * NCHILD) * sizeof(int))) ==
204 	    NULL) {
205 		tst_brkm(TWARN, NULL,
206 			 "\tMalloc failed (may be OK if under stress)");
207 	}
208 
209 	child_count = 0;
210 	for (j = 0; j < child_groups; j++) {
211 		for (i = 0; i < NCHILD; i++) {
212 			getchild(j, i, child_count);
213 			child_count++;
214 		}
215 	}
216 
217 	/* If signal already received, skip to cleanup */
218 
219 	if (!sigchld && !sigterm) {
220 		if (test_time) {
221 			/* To get out of sleep if signal caught */
222 			if (!setjmp(env_buf)) {
223 				jump++;
224 				sleep(test_time);
225 			}
226 		} else {
227 			pause();
228 		}
229 	}
230 
231 	/* Reset signals since we are about to clean-up and to avoid
232 	 * problem with wait call *               $
233 	 * */
234 
235 	if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
236 		tst_brkm(TFAIL, cleanup,
237 			 "Error resetting SIGTERM signal, ERRNO = %d", errno);
238 	}
239 	if (signal(SIGCLD, SIG_DFL) == SIG_ERR) {
240 		tst_brkm(TFAIL, cleanup,
241 			 "Error resetting SIGCLD signal, ERRNO = %d", errno);
242 	}
243 
244 	if (test_time) {
245 		sleep(test_time);
246 	}
247 
248 	/* Clean up children */
249 	massmurder();
250 	/*
251 	 * Watch children finish and show returns.
252 	 */
253 
254 	count = 0;
255 	while (1) {
256 		if ((child = wait(&status)) > 0) {
257 			if (status != 0) {
258 				tst_brkm(TWARN,
259 					 NULL,
260 					 "\tChild{%d} exited status = %0x",
261 					 child, status);
262 			}
263 			count++;
264 		} else {
265 			if (errno != EINTR) {
266 				break;
267 			}
268 			tst_resm(TINFO, "\tSignal detected during wait");
269 		}
270 	}
271 
272 	/*
273 	 * Make sure correct number of children exited.
274 	 */
275 
276 	if (count != child_count) {
277 		tst_resm(TWARN, "\tWrong number of children waited on!");
278 		tst_brkm(TWARN, NULL, "\tSaw %d, expected %d", count,
279 			 NCHILD);
280 	}
281 
282 	/* Check for core file in test directory. */
283 
284 	if (access("core", 0) == 0) {
285 		tst_brkm(TWARN, NULL, "\tCore file found in test directory.");
286 	}
287 
288 	/* Remove expected files */
289 
290 	for (j = 0; j < nfiles; j += NCHILD) {
291 		sprintf(tmpdir, DIR_NAME, j);
292 		if (rmdir(tmpdir) < 0) {
293 			tst_brkm(TWARN,
294 				 NULL,
295 				 "\tError removing expected directory, ERRNO = %d",
296 				 errno);
297 		}
298 	}
299 
300 	tst_resm(TPASS, "PASS");
301 
302 	return 0;
303 }
304 
getchild(int group,int child,int children)305 int getchild(int group, int child, int children)
306 {
307 	int pid;
308 
309 	pid = FORK_OR_VFORK();
310 
311 	if (pid < 0) {
312 
313 		massmurder();	/* kill the kids */
314 		tst_brkm(TBROK, cleanup,
315 			 "\tFork failed (may be OK if under stress)");
316 	} else if (pid == 0) {	/* child does this */
317 		switch (children % NCHILD) {
318 		case 0:
319 #ifdef UCLINUX
320 			if (self_exec(argv0, "nd", 1, nfiles) < 0) {
321 				massmurder();
322 				tst_brkm(TBROK, cleanup, "\tself_exec failed");
323 			}
324 #else
325 			dochild1();	/* create existing directories */
326 #endif
327 			break;	/* so lint won't complain */
328 		case 1:
329 #ifdef UCLINUX
330 			if (self_exec(argv0, "n", 2) < 0) {
331 				massmurder();
332 				tst_brkm(TBROK, cleanup, "\tself_exec failed");
333 			}
334 #else
335 			dochild2();	/* remove nonexistant directories */
336 #endif
337 			break;
338 		case 2:
339 #ifdef UCLINUX
340 			if (self_exec(argv0, "nd", 3, group) < 0) {
341 				massmurder();
342 				tst_brkm(TBROK, cleanup, "\tself_exec failed");
343 			}
344 #else
345 			dochild3(group);	/* create/delete directories */
346 #endif
347 			break;
348 		default:
349 			tst_brkm(TFAIL, cleanup,
350 				 "Test not inplemented for child %d", child);
351 			exit(1);
352 			break;
353 		}
354 		exit(1);	/* If child gets here, something wrong */
355 	}
356 	pidlist[children] = pid;
357 	return 0;
358 }
359 
term(int sig)360 void term(int sig)
361 {
362 	/* Routine to handle SIGTERM signal. */
363 
364 	if (parent_pid == getpid()) {
365 		tst_brkm(TWARN, NULL, "\tsignal SIGTERM received by parent.");
366 	}
367 	sigterm++;
368 	if (jump) {
369 		longjmp(env_buf, 1);
370 	}
371 }
372 
chld(int sig)373 void chld(int sig)
374 {
375 	/* Routine to handle SIGCLD signal. */
376 
377 	sigchld++;
378 	if (jump) {
379 		longjmp(env_buf, 1);
380 	}
381 }
382 
dochild1(void)383 int dochild1(void)
384 {
385 	/* Child routine which attempts to create directories in the test
386 	 * directory that already exist. Runs until a SIGTERM signal is
387 	 * received. Will exit with an error if it is able to create the
388 	 * directory or if the expected error is not received.
389 	 */
390 
391 	int j;
392 	char tmpdir[MAXPATHLEN];
393 
394 	while (!sigterm) {
395 		for (j = 0; j < nfiles; j += NCHILD) {
396 			sprintf(tmpdir, DIR_NAME, j);
397 			TEST(mkdir(tmpdir, MODE_RWX));
398 
399 			if (TEST_RETURN < 0) {
400 
401 				if (TEST_ERRNO != EEXIST) {
402 					tst_brkm(TFAIL, cleanup,
403 						 "MKDIR %s, errno = %d; Wrong error detected.",
404 						 tmpdir, TEST_ERRNO);
405 					exit(1);
406 				}
407 			} else {
408 				tst_brkm(TFAIL, cleanup,
409 					 "MKDIR %s succeded when it shoud have failed.",
410 					 tmpdir);
411 				exit(1);
412 			}
413 		}
414 	}
415 	exit(0);
416 }
417 
418 #ifdef UCLINUX
dochild1_uclinux(void)419 void dochild1_uclinux(void)
420 {
421 	/* Set up to catch SIGTERM signal */
422 	if (signal(SIGTERM, term) == SIG_ERR) {
423 		tst_brkm(TFAIL, cleanup,
424 			 "Error setting up SIGTERM signal, ERRNO = %d", errno);
425 	}
426 
427 	dochild1();
428 }
429 #endif
430 
dochild2(void)431 int dochild2(void)
432 {
433 	/* Child routine which attempts to remove directories from the
434 	 * test directory which do not exist. Runs until a SIGTERM
435 	 * signal is received. Exits with an error if the proper
436 	 * error is not detected or if the remove operation is
437 	 * successful.
438 	 */
439 
440 	int j;
441 	char tmpdir[MAXPATHLEN];
442 
443 	while (!sigterm) {
444 		for (j = 1; j < nfiles; j += NCHILD) {
445 			sprintf(tmpdir, DIR_NAME, j);
446 			if (rmdir(tmpdir) < 0) {
447 				if (errno != ENOENT) {
448 					tst_brkm(TFAIL, cleanup,
449 						 "RMDIR %s, errno = %d; Wrong error detected.",
450 						 tmpdir, errno);
451 					exit(1);
452 				}
453 			} else {
454 				tst_brkm(TFAIL, cleanup,
455 					 "RMDIR %s succeded when it should have failed.",
456 					 tmpdir);
457 				exit(1);
458 			}
459 		}
460 	}
461 	exit(0);
462 	return 0;
463 }
464 
465 #ifdef UCLINUX
dochild2_uclinux(void)466 void dochild2_uclinux(void)
467 {
468 	/* Set up to catch SIGTERM signal */
469 	if (signal(SIGTERM, term) == SIG_ERR) {
470 		tst_brkm(TFAIL, cleanup,
471 			 "Error setting up SIGTERM signal, ERRNO = %d", errno);
472 	}
473 
474 	dochild2();
475 }
476 #endif
477 
dochild3(int group)478 int dochild3(int group)
479 {
480 	/* Child routine which creates and deletes directories in the
481 	 * test directory. Runs until a SIGTERM signal is received, then
482 	 * cleans up and exits. Detects error if the expected condition
483 	 * is not encountered.
484 	 */
485 
486 	int j;
487 
488 	char tmpdir[MAXPATHLEN];
489 	char tmp[MAXPATHLEN];
490 
491 	while (!sigterm) {
492 		for (j = 2; j < nfiles; j += NCHILD) {
493 			strcpy(tmp, DIR_NAME);
494 			strcat(tmp, ".%d");
495 			sprintf(tmpdir, tmp, j, group);
496 
497 			TEST(mkdir(tmpdir, MODE_RWX));
498 
499 			if (TEST_RETURN < 0) {
500 				tst_brkm(TFAIL, cleanup,
501 					 "MKDIR %s, errno = %d; Wrong error detected.",
502 					 tmpdir, TEST_ERRNO);
503 				exit(1);
504 			}
505 		}
506 		for (j = 2; j < nfiles; j += NCHILD) {
507 			strcpy(tmp, DIR_NAME);
508 			strcat(tmp, ".%d");
509 			sprintf(tmpdir, tmp, j, group);
510 			if (rmdir(tmpdir) < 0) {
511 				tst_brkm(TFAIL, cleanup,
512 					 "RMDIR %s, errno = %d; Wrong error detected.",
513 					 tmpdir, errno);
514 				exit(1);
515 			}
516 		}
517 	}
518 	exit(0);
519 }
520 
521 #ifdef UCLINUX
dochild3_uclinux(void)522 void dochild3_uclinux(void)
523 {
524 	/* Set up to catch SIGTERM signal */
525 	if (signal(SIGTERM, term) == SIG_ERR) {
526 		tst_brkm(TFAIL, cleanup,
527 			 "Error setting up SIGTERM signal, ERRNO = %d", errno);
528 	}
529 
530 	dochild3(group_uclinux);
531 }
532 #endif
533 
massmurder(void)534 int massmurder(void)
535 {
536 	register int j;
537 	for (j = 0; j < child_count; j++) {
538 		if (pidlist[j] > 0) {
539 			if (kill(pidlist[j], SIGTERM) < 0) {
540 				tst_brkm(TFAIL, cleanup,
541 					 "Error killing child %d, ERRNO = %d",
542 					 j, errno);
543 			}
544 		}
545 	}
546 	return 0;
547 }
548 
549 /***************************************************************
550  *  * setup() - performs all ONE TIME setup for this test.
551  *   ***************************************************************/
setup(void)552 void setup(void)
553 {
554 
555 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
556 
557 	TEST_PAUSE;
558 
559 	/* Create a temporary directory and make it current. */
560 	tst_tmpdir();
561 
562 }
563 
564 /***************************************************************
565  *  * cleanup() - performs all ONE TIME cleanup for this test at
566  *   *              completion or premature exit.
567  *    ***************************************************************/
cleanup(void)568 void cleanup(void)
569 {
570 
571 	/*
572 	 *      * Remove the temporary directory.
573 	 *           */
574 	tst_rmdir();
575 
576 	/*
577 	 *      * Exit with return code appropriate for results.
578 	 *           */
579 
580 }
581