blob: de1cb6971651c5162d69b96bdd8f5c430632ec8a [file] [log] [blame]
/*
*
* Copyright (c) International Business Machines Corp., 2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
/* 10/30/2002 Port to LTP dbarrera@us.ibm.com */
/*
* NAME
* mkdir.c - Stress test of mkdir call.
*
* CALLS
* mkdir, rmdir
*
* ALGORITHM
* Create multiple processes which create subdirectories in the
* same directory multiple times. On exit of all child processes,
* make sure all subdirectories can be removed.
*
* USAGE: mkdir09 -c # -t # -d #
* -c = number of children groups
* -t = number of seconds to run test
* -d = number of directories created in test directory
*
* RESTRICTIONS
*
*/
#include <stdio.h> /* needed by testhead.h */
#include <sys/wait.h> /* needed by testhead.h */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
#include "test.h"
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#define NCHILD 3
#define MODE_RWX 07770
#define DIR_NAME "./X.%d"
/* used by getopt */
extern char *optarg;
extern int optind, opterr;
char *goodopts = "c:t:d:";
int errflg;
char *TCID = "mkdir09";
int TST_TOTAL = 1;
int child_groups, test_time, nfiles;
char testdir[MAXPATHLEN];
int parent_pid, sigchld, sigterm, jump;
void term(int sig);
void chld(int sig);
int *pidlist, child_count;
jmp_buf env_buf;
int getchild(int group, int child, int children);
int dochild1(void);
int dochild2(void);
int dochild3(int group);
int massmurder(void);
int runtest(void);
void setup(void);
void cleanup(void);
#ifdef UCLINUX
static char *argv0;
void dochild1_uclinux(void);
void dochild2_uclinux(void);
void dochild3_uclinux(void);
static int group_uclinux;
#endif
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
int main(int argc, char *argv[])
{
int c;
#ifdef UCLINUX
tst_parse_opts(argc, argv, NULL, NULL);
argv0 = argv[0];
maybe_run_child(&dochild1_uclinux, "nd", 1, &nfiles);
maybe_run_child(&dochild2_uclinux, "n", 2);
maybe_run_child(&dochild3_uclinux, "nd", 3, &group_uclinux);
#endif
setup();
/* Set up to catch SIGTERM signal */
if (signal(SIGTERM, term) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error setting up SIGTERM signal, ERRNO = %d", errno);
}
/* Set up to catch SIGCHLD signal */
if (signal(SIGCHLD, chld) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error setting up SIGCHLD signal, ERRNO = %d", errno);
}
/* Default argument settings. */
child_groups = 2;
test_time = 5; /* 0 = run forever or till signal */
nfiles = 5;
/* Get command line options */
while ((c = getopt(argc, argv, goodopts)) != EOF) {
switch (c) {
case 'c':
child_groups = atoi(optarg);
break;
case 't':
test_time = atoi(optarg);
break;
case 'd':
nfiles = atoi(optarg);
break;
case '?':
errflg++;
break;
default:
break;
}
}
if (errflg) {
tst_resm(TINFO,
"USAGE : mkdir09 -c #child_groups -t#test_time -d#directories");
tst_resm(TINFO, "Bad argument count.");
}
runtest();
cleanup();
tst_exit();
}
/*--------------------------------------------------------------*/
int runtest(void)
{
int i, j;
int count, child, status;
char tmpdir[MAXPATHLEN];
/* Create permanent directories with holes in directory structure */
for (j = 0; j < nfiles; j++) {
sprintf(tmpdir, DIR_NAME, j);
TEST(mkdir(tmpdir, MODE_RWX));
if (TEST_RETURN < 0) {
tst_brkm(TFAIL, cleanup,
"Error creating permanent directories, ERRNO = %d",
TEST_ERRNO);
}
if ((j % NCHILD) != 0) {
if (rmdir(tmpdir) < 0) {
tst_brkm(TFAIL, cleanup,
"Error removing directory, ERRNO = %d",
errno);
}
}
}
parent_pid = getpid();
/* allocate space for list of child pid's */
if ((pidlist = malloc((child_groups * NCHILD) * sizeof(int))) ==
NULL) {
tst_brkm(TWARN, NULL,
"\tMalloc failed (may be OK if under stress)");
}
child_count = 0;
for (j = 0; j < child_groups; j++) {
for (i = 0; i < NCHILD; i++) {
getchild(j, i, child_count);
child_count++;
}
}
/* If signal already received, skip to cleanup */
if (!sigchld && !sigterm) {
if (test_time) {
/* To get out of sleep if signal caught */
if (!setjmp(env_buf)) {
jump++;
sleep(test_time);
}
} else {
pause();
}
}
/* Reset signals since we are about to clean-up and to avoid
* problem with wait call * $
* */
if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error resetting SIGTERM signal, ERRNO = %d", errno);
}
if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error resetting SIGCHLD signal, ERRNO = %d", errno);
}
if (test_time) {
sleep(test_time);
}
/* Clean up children */
massmurder();
/*
* Watch children finish and show returns.
*/
count = 0;
while (1) {
if ((child = wait(&status)) > 0) {
if (status != 0) {
tst_brkm(TWARN,
NULL,
"\tChild{%d} exited status = %0x",
child, status);
}
count++;
} else {
if (errno != EINTR) {
break;
}
tst_resm(TINFO, "\tSignal detected during wait");
}
}
/*
* Make sure correct number of children exited.
*/
if (count != child_count) {
tst_resm(TWARN, "\tWrong number of children waited on!");
tst_brkm(TWARN, NULL, "\tSaw %d, expected %d", count,
NCHILD);
}
/* Check for core file in test directory. */
if (access("core", 0) == 0) {
tst_brkm(TWARN, NULL, "\tCore file found in test directory.");
}
/* Remove expected files */
for (j = 0; j < nfiles; j += NCHILD) {
sprintf(tmpdir, DIR_NAME, j);
if (rmdir(tmpdir) < 0) {
tst_brkm(TWARN,
NULL,
"\tError removing expected directory, ERRNO = %d",
errno);
}
}
tst_resm(TPASS, "PASS");
return 0;
}
int getchild(int group, int child, int children)
{
int pid;
pid = FORK_OR_VFORK();
if (pid < 0) {
massmurder(); /* kill the kids */
tst_brkm(TBROK, cleanup,
"\tFork failed (may be OK if under stress)");
} else if (pid == 0) { /* child does this */
switch (children % NCHILD) {
case 0:
#ifdef UCLINUX
if (self_exec(argv0, "nd", 1, nfiles) < 0) {
massmurder();
tst_brkm(TBROK, cleanup, "\tself_exec failed");
}
#else
dochild1(); /* create existing directories */
#endif
break; /* so lint won't complain */
case 1:
#ifdef UCLINUX
if (self_exec(argv0, "n", 2) < 0) {
massmurder();
tst_brkm(TBROK, cleanup, "\tself_exec failed");
}
#else
dochild2(); /* remove nonexistant directories */
#endif
break;
case 2:
#ifdef UCLINUX
if (self_exec(argv0, "nd", 3, group) < 0) {
massmurder();
tst_brkm(TBROK, cleanup, "\tself_exec failed");
}
#else
dochild3(group); /* create/delete directories */
#endif
break;
default:
tst_brkm(TFAIL, cleanup,
"Test not inplemented for child %d", child);
exit(1);
break;
}
exit(1); /* If child gets here, something wrong */
}
pidlist[children] = pid;
return 0;
}
void term(int sig)
{
/* Routine to handle SIGTERM signal. */
if (parent_pid == getpid()) {
tst_brkm(TWARN, NULL, "\tsignal SIGTERM received by parent.");
}
sigterm++;
if (jump) {
longjmp(env_buf, 1);
}
}
void chld(int sig)
{
/* Routine to handle SIGCHLD signal. */
sigchld++;
if (jump) {
longjmp(env_buf, 1);
}
}
int dochild1(void)
{
/* Child routine which attempts to create directories in the test
* directory that already exist. Runs until a SIGTERM signal is
* received. Will exit with an error if it is able to create the
* directory or if the expected error is not received.
*/
int j;
char tmpdir[MAXPATHLEN];
while (!sigterm) {
for (j = 0; j < nfiles; j += NCHILD) {
sprintf(tmpdir, DIR_NAME, j);
TEST(mkdir(tmpdir, MODE_RWX));
if (TEST_RETURN < 0) {
if (TEST_ERRNO != EEXIST) {
tst_brkm(TFAIL, cleanup,
"MKDIR %s, errno = %d; Wrong error detected.",
tmpdir, TEST_ERRNO);
exit(1);
}
} else {
tst_brkm(TFAIL, cleanup,
"MKDIR %s succeded when it shoud have failed.",
tmpdir);
exit(1);
}
}
}
exit(0);
}
#ifdef UCLINUX
void dochild1_uclinux(void)
{
/* Set up to catch SIGTERM signal */
if (signal(SIGTERM, term) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error setting up SIGTERM signal, ERRNO = %d", errno);
}
dochild1();
}
#endif
int dochild2(void)
{
/* Child routine which attempts to remove directories from the
* test directory which do not exist. Runs until a SIGTERM
* signal is received. Exits with an error if the proper
* error is not detected or if the remove operation is
* successful.
*/
int j;
char tmpdir[MAXPATHLEN];
while (!sigterm) {
for (j = 1; j < nfiles; j += NCHILD) {
sprintf(tmpdir, DIR_NAME, j);
if (rmdir(tmpdir) < 0) {
if (errno != ENOENT) {
tst_brkm(TFAIL, cleanup,
"RMDIR %s, errno = %d; Wrong error detected.",
tmpdir, errno);
exit(1);
}
} else {
tst_brkm(TFAIL, cleanup,
"RMDIR %s succeded when it should have failed.",
tmpdir);
exit(1);
}
}
}
exit(0);
return 0;
}
#ifdef UCLINUX
void dochild2_uclinux(void)
{
/* Set up to catch SIGTERM signal */
if (signal(SIGTERM, term) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error setting up SIGTERM signal, ERRNO = %d", errno);
}
dochild2();
}
#endif
int dochild3(int group)
{
/* Child routine which creates and deletes directories in the
* test directory. Runs until a SIGTERM signal is received, then
* cleans up and exits. Detects error if the expected condition
* is not encountered.
*/
int j;
char tmpdir[MAXPATHLEN];
char tmp[MAXPATHLEN];
while (!sigterm) {
for (j = 2; j < nfiles; j += NCHILD) {
strcpy(tmp, DIR_NAME);
strcat(tmp, ".%d");
sprintf(tmpdir, tmp, j, group);
TEST(mkdir(tmpdir, MODE_RWX));
if (TEST_RETURN < 0) {
tst_brkm(TFAIL, cleanup,
"MKDIR %s, errno = %d; Wrong error detected.",
tmpdir, TEST_ERRNO);
exit(1);
}
}
for (j = 2; j < nfiles; j += NCHILD) {
strcpy(tmp, DIR_NAME);
strcat(tmp, ".%d");
sprintf(tmpdir, tmp, j, group);
if (rmdir(tmpdir) < 0) {
tst_brkm(TFAIL, cleanup,
"RMDIR %s, errno = %d; Wrong error detected.",
tmpdir, errno);
exit(1);
}
}
}
exit(0);
}
#ifdef UCLINUX
void dochild3_uclinux(void)
{
/* Set up to catch SIGTERM signal */
if (signal(SIGTERM, term) == SIG_ERR) {
tst_brkm(TFAIL, cleanup,
"Error setting up SIGTERM signal, ERRNO = %d", errno);
}
dochild3(group_uclinux);
}
#endif
int massmurder(void)
{
register int j;
for (j = 0; j < child_count; j++) {
if (pidlist[j] > 0) {
if (kill(pidlist[j], SIGTERM) < 0) {
tst_brkm(TFAIL, cleanup,
"Error killing child %d, ERRNO = %d",
j, errno);
}
}
}
return 0;
}
/***************************************************************
* * setup() - performs all ONE TIME setup for this test.
* ***************************************************************/
void setup(void)
{
tst_sig(NOFORK, DEF_HANDLER, cleanup);
TEST_PAUSE;
/* Create a temporary directory and make it current. */
tst_tmpdir();
}
/***************************************************************
* * cleanup() - performs all ONE TIME cleanup for this test at
* * completion or premature exit.
* ***************************************************************/
void cleanup(void)
{
/*
* * Remove the temporary directory.
* */
tst_rmdir();
/*
* * Exit with return code appropriate for results.
* */
}