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