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 */ 66extern char *optarg; 67extern int optind, opterr; 68char *goodopts = "c:t:d:"; 69int errflg; 70 71char *TCID = "mkdir09"; 72int TST_TOTAL = 1; 73 74int child_groups, test_time, nfiles; 75char testdir[MAXPATHLEN]; 76int parent_pid, sigchld, sigterm, jump; 77void term(int sig); 78void chld(int sig); 79int *pidlist, child_count; 80jmp_buf env_buf; 81 82int getchild(int group, int child, int children); 83int dochild1(void); 84int dochild2(void); 85int dochild3(int group); 86int massmurder(void); 87int runtest(void); 88void setup(void); 89void cleanup(void); 90 91#ifdef UCLINUX 92static char *argv0; 93void dochild1_uclinux(void); 94void dochild2_uclinux(void); 95void dochild3_uclinux(void); 96static int group_uclinux; 97#endif 98 99/*--------------------------------------------------------------*/ 100/*--------------------------------------------------------------*/ 101/*--------------------------------------------------------------*/ 102int 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 173int 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 305int 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 360void 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 373void chld(int sig) 374{ 375 /* Routine to handle SIGCLD signal. */ 376 377 sigchld++; 378 if (jump) { 379 longjmp(env_buf, 1); 380 } 381} 382 383int 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 419void 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 431int 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 466void 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 478int 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 522void 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 534int 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 * ***************************************************************/ 552void 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 * ***************************************************************/ 568void 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