/*
*
* 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
*/
/* 11/01/2002 Port to LTP robbiew@us.ibm.com */
/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
/*inode02.c */
/*======================================================================
=================== TESTPLAN SEGMENT ===================
CALLS: mkdir, stat, open
Run with TERM mode.
>KEYS: < file system and I/O management, system resource constraints.
>WHAT: < Can the system handle a heavy load on the file system I/O
< functions?
>HOW: < Create several identical process that call inode02.c. This
< will simulate the multi-user environment, and hopefully uncover
< conflicts that might occur in "real life" use.
>BUGS: <
======================================================================*/
#define PATH_STRING_LENGTH 1024
#define NAME_LENGTH 8
#define MAX_PATH_STRING_LENGTH (PATH_STRING_LENGTH - NAME_LENGTH - 40)
#define DIRECTORY_MODE 00777
#define FILE_MODE 00777
#define MKDIR_STRING_LENGTH (MAX_PATH_STRING_LENGTH + 7)
/* #define DEBUG you can watch the generation with this flag */
#define TRUE 1
#define FALSE 0
#define READ 0
#define WRITE 1
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#ifdef LINUX
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#endif
#define MAXCHILD 25
int allchild[MAXCHILD + 1];
char name[NAME_LENGTH + 1];
char path_string[PATH_STRING_LENGTH + 1];
char read_string[PATH_STRING_LENGTH + 1];
char write_string[PATH_STRING_LENGTH + 1];
char remove_string[PATH_STRING_LENGTH + 10];
int parent_pid;
int nchild;
FILE *list_stream = NULL;
int list_id;
int file_id;
int increment_name(), get_next_name(), mode(), escrivez(), massmurder();
int max_depth, max_breadth, file_length;
int bd_arg(char *);
#ifdef LINUX
void (*sigset(int, void (*)(int))) (int);
#endif
/** LTP Port **/
#include "test.h"
void setup(void);
void fail_exit(void);
void anyfail(void);
void ok_exit(void);
void forkfail(void);
void terror(char *);
int instress(void);
#define FAILED 0
#define PASSED 1
int local_flag = PASSED;
FILE *temp;
char *TCID = "inode02"; /* Test program identifier. */
int TST_TOTAL = 1; /* Total number of test cases. */
/**************/
int main(int argc, char *argv[])
{
int pid, tree(), p, status;
int count, child;
register int i;
int term();
setup();
parent_pid = getpid();
if (sigset(SIGTERM, (void (*)())term) == SIG_ERR) {
tst_resm(TBROK, "\tSIGTERM sigset set failed, errno=%d\n",
errno);
exit(1);
}
/************************************************/
/* */
/* Input the parameters for the directory--- */
/* file trees which are to be generated */
/* */
/************************************************/
if (argc < 2) {
max_depth = 6;
max_breadth = 5;
file_length = 8;
nchild = 5;
} else if (argc < 5) {
tst_resm(TCONF, "Bad argument count.\n");
printf
("\tinode02 max_depth max_breadth file_length #children\n\tdefault: inode02 6 5 8 5\n");
exit(1);
} else {
i = 1;
if (sscanf(argv[i++], "%d", &max_depth) != 1)
bd_arg(argv[i - 1]);
if (sscanf(argv[i++], "%d", &max_breadth) != 1)
bd_arg(argv[i - 1]);
if (sscanf(argv[i++], "%d", &file_length) != 1)
bd_arg(argv[i - 1]);
if (sscanf(argv[i++], "%d", &nchild) != 1)
bd_arg(argv[i - 1]);
if (nchild > MAXCHILD) {
fprintf(temp, "too many children - max is %d\n",
MAXCHILD);
exit(1);
}
}
/************************************************/
/* */
/* Generate and check nchild trees */
/* */
/************************************************/
for (p = 0; p < nchild; p++) {
pid = fork();
if (pid == 0) {
tree();
} else {
if (pid < 1) {
terror
("Fork failed (may be OK if under stress)");
massmurder();
if (instress()) {
ok_exit();
}
forkfail();
}
}
}
count = 0;
while ((child = wait(&status)) > 0) {
#ifdef DEBUG
tst_resm(TINFO, "Test %d exited status = %d\n", child, status);
#endif
if (status) {
fprintf(temp, "Test %d failed - expected 0 exit.\n",
child);
local_flag = FAILED;
}
count++;
}
if (count != nchild) {
tst_resm(TFAIL, "Wrong number of children waited on!\n");
tst_resm(TFAIL, "Saw %d, expected %d\n", count, nchild);
local_flag = FAILED;
}
/************************************************/
/* */
/* And report the results.......... */
/* */
/************************************************/
anyfail();
/** NOT REACHED **/
tst_exit();
}
int bd_arg(char *str)
{
fprintf(temp,
"Bad argument - %s - could not parse as number.\n\tinode02 [max_depth] [max_breadth] [file_length] [#children]\n\tdefault: inode02 6 5 8 5\n",
str);
exit(1);
}
int tree(void)
/************************************************/
/* */
/* TREE */
/* */
/* generate a tree of directories and files */
/* and save the path names in the path_list */
/* file */
/* */
/* then, read the path names and attempt to */
/* access the corresponding directories and */
/* files */
/* */
/************************************************/
{
int gen_ret_val, ch_ret_val, exit_val, level;
int ret_val;
int generate(), check();
char path_list_string[PATH_STRING_LENGTH + 10];
int len;
int status;
int snp_ret;
/********************************/
/* */
/* make the root directory for */
/* the tree */
/* */
/********************************/
sprintf(path_string, "inode02.%d", getpid());
ret_val = mkdir(path_string, DIRECTORY_MODE);
if (ret_val == -1) {
tst_resm(TBROK,
"Reason: Impossible to create directory %s, errno=%d\n",
path_string, errno);
exit(-5);
}
strcpy(remove_string, "rm -rf ");
strcat(remove_string, path_string);
#ifdef DEBUG
tst_resm(TINFO, "\n%s\n", path_string);
#endif
/****************************************/
/* */
/* create the "path_list" file, in */
/* which the list of generated paths */
/* will be stored so that they later */
/* may be checked */
/* */
/****************************************/
snp_ret = snprintf(path_list_string, sizeof(path_list_string),
"%s/path_list", path_string);
if (snp_ret < 0 || snp_ret >= sizeof(path_list_string)) {
tst_resm(TBROK, "snprintf(path_list_string,..) returned %d",
snp_ret);
exit(-1);
}
list_id = creat(path_list_string, FILE_MODE);
if (list_id == -1) {
fprintf(temp,
"\nThe path_list file '%s' cannot be created, errno=%d\n",
path_list_string, errno);
exit(-7);
}
/****************************************/
/* */
/* and store its name in path_list */
/* */
/****************************************/
strcpy(write_string, path_string);
len = strlen(write_string);
write_string[len++] = 'D';
write_string[len] = '\0';
escrivez(write_string);
/****************************************/
/* */
/* generate the directory-file tree */
/* */
/****************************************/
level = 0;
#ifdef DEBUG
tst_resm(TINFO, "\n\t%s\n\n", "GENERATING:");
#endif
gen_ret_val = generate(path_string, level);
close(list_id);
list_id = open(path_list_string, READ);
if (list_id == -1) {
fprintf(temp,
"\nThe path_list file cannot be opened for reading, errno=%d\n",
errno);
exit(-8);
}
list_stream = fdopen(list_id, "r");
/****************************************/
/* */
/* check the directory-file tree */
/* for correctness */
/* */
/****************************************/
#ifdef DEBUG
tst_resm(TINFO, "\n\t%s\n\n", "CHECKING:");
#endif
ch_ret_val = check();
exit_val = MIN(ch_ret_val, gen_ret_val);
status = fclose(list_stream);
if (status != 0) {
fprintf(temp,
"Failed to close list_stream: ret=%d errno=%d (%s)\n",
status, errno, strerror(errno));
exit(-8);
}
/*
* Remove file.
*/
status = system(remove_string);
if (status) {
fprintf(temp, "Caution - `%s' failed.\n", remove_string);
fprintf(temp, "Status returned %d.\n", status);
}
/****************************************/
/* */
/* .....and exit main */
/* */
/****************************************/
exit(exit_val);
}
int generate(char *string, int level)
/****************************************/
/* */
/* generate recursively a tree of */
/* directories and files: within */
/* created directory, an alternating */
/* series of files and directories */
/* are constructed---until tree */
/* breadth and depth limits are */
/* reached or an error occurs */
/* */
/****************************************/
/***************************/
/* string: */
/* the directory path */
/* string below which a */
/* tree is generated */
/* */
/***************************/
/***************************/
/* level: */
/* the tree depth variable */
/* */
/***************************/
{
int switch_flag;
int ret_val = 0;
int new_ret_val, len, ret_len;
char new_string[PATH_STRING_LENGTH + 1];
int new_level;
int i, j; /* iteration counters */
int snp_ret;
switch_flag = level & TRUE;
if (strlen(string) >= MAX_PATH_STRING_LENGTH) {
/********************************/
/* */
/* Maximum path name length */
/* reached */
/* */
/********************************/
fprintf(temp, "\nMaximum path_name length reached\n");
return (-1);
} else if (level < max_depth) {
for (i = 0; i <= max_breadth; i++) {
get_next_name();
snp_ret = snprintf(new_string, sizeof(new_string),
"%s/%s", string, name);
if (snp_ret < 0 || snp_ret >= sizeof(new_string)) {
tst_resm(TBROK, "snprintf(new_string,..) "
"returned %d", snp_ret);
exit(-1);
}
/****************************************/
/* */
/* switch between creating files */
/* and making directories */
/* */
/****************************************/
if (switch_flag) {
switch_flag = FALSE;
/****************************************/
/* */
/* create a new file */
/* */
/****************************************/
file_id = creat(new_string, FILE_MODE);
if (file_id == -1) {
fprintf(temp,
"\nImpossible to create file %s, errno=%d\n",
new_string, errno);
return (-2);
}
#ifdef DEBUG
tst_resm(TINFO, "%d %s F\n", level,
new_string);
#endif
/****************************************/
/* */
/* write to it */
/* */
/****************************************/
len = strlen(new_string);
for (j = 1; j <= file_length; j++) {
ret_len =
write(file_id, new_string, len);
if (ret_len != len) {
fprintf(temp,
"\nUnsuccessful write to file %s, errno=%d\n",
new_string, errno);
return (-3);
}
}
close(file_id);
/****************************************/
/* */
/* and store its name in path_list */
/* */
/****************************************/
strcpy(write_string, new_string);
len = strlen(write_string);
write_string[len++] = 'F';
write_string[len] = '\0';
escrivez(write_string);
} else {
switch_flag = TRUE;
/****************************************/
/* */
/* or make a directory */
/* */
/* (mknod can only be called when in */
/* super user mode) */
/* */
/****************************************/
ret_val = mkdir(new_string, DIRECTORY_MODE);
if (ret_val != 0) {
fprintf(temp,
"\nImpossible to create directory %s, errno=%d\n",
new_string, errno);
return (-5);
}
#ifdef DEBUG
tst_resm(TINFO, "%d %s D\n", level,
new_string);
#endif
/****************************************/
/* */
/* store its name in path_list */
/* */
/****************************************/
strcpy(write_string, new_string);
len = strlen(write_string);
write_string[len++] = 'D';
write_string[len] = '\0';
escrivez(write_string);
/****************************************/
/* */
/* and generate a new level */
/* */
/****************************************/
new_level = level + 1;
new_ret_val = generate(new_string, new_level);
if (new_ret_val < ret_val)
ret_val = new_ret_val;
}
}
/********************************/
/* */
/* Maximum breadth reached */
/* */
/********************************/
return (ret_val);
} else
/********************************/
/* */
/* Maximum depth reached */
/* */
/********************************/
return 0;
}
int check(void)
/****************************************/
/* */
/* check for file and directory */
/* correctness by reading records */
/* from the path_list and attempting */
/* to determine if the corresponding */
/* files or directories are as */
/* created */
/* */
/****************************************/
{
int len, path_mode, val, ret_len, j;
for (;;) {
/****************************************/
/* */
/* read a path string from path_list */
/* */
/****************************************/
if (fscanf(list_stream, "%s", path_string) == EOF) {
#ifdef DEBUG
tst_resm(TINFO, "\nEnd of path_list file reached \n");
#endif
return 0;
}
#ifdef DEBUG
tst_resm(TINFO, "%s\n", path_string);
#endif
len = strlen(path_string);
len--;
if (path_string[len] == 'F') {
/********************************/
/* */
/* this should be a file */
/* */
/********************************/
path_string[len] = '\0';
file_id = open(path_string, READ);
if (file_id <= 0) {
fprintf(temp,
"\nImpossible to open file %s, errno=%d\n",
path_string, errno);
return (-1);
}
else {
/********************************/
/* */
/* check its contents */
/* */
/********************************/
ret_len = 0;
len = strlen(path_string);
for (j = 1; j <= file_length; j++) {
ret_len =
read(file_id, read_string, len);
if (len != ret_len) {
fprintf(temp,
"\nFile read error for file %s, errno=%d\n",
path_string, errno);
return (-3);
}
read_string[len] = '\0';
val = strcmp(read_string, path_string);
if (val != 0) {
fprintf(temp,
"\nContents of file %s are different than expected: %s\n",
path_string,
read_string);
return (-4);
}
}
close(file_id);
} /* else for */
if (ret_len <= 0) {
fprintf(temp,
"\nImpossible to read file %s, errno=%d\n",
path_string, errno);
return (-2);
}
} else {
/********************************/
/* */
/* otherwise.......... */
/* it should be a directory */
/* */
/********************************/
path_string[len] = '\0';
path_mode = mode(path_string);
if (path_mode == -1) {
fprintf(temp,
"\nPreviously created directory path %s was not open\n",
path_string);
return (-4);
}
if ((040000 & path_mode) != 040000) {
fprintf(temp,
"\nPath %s was not recognized to be a directory\n",
path_string);
fprintf(temp, "Its mode is %o\n", path_mode);
return (-5);
}
}
} /* while */
}
int get_next_name(void)
/****************************************/
/* */
/* get the next---in a dictionary */
/* sense---file or directory name */
/* */
/****************************************/
{
static int k;
int i;
int last_position;
last_position = NAME_LENGTH - 1;
if (k == 0) {
/************************/
/* */
/* initialize name */
/* */
/************************/
for (i = 0; i < NAME_LENGTH; i++)
name[i] = 'a';
name[NAME_LENGTH] = '\0';
k++;
}
/********************************/
/* */
else
increment_name(last_position); /* i.e., beginning at the last */
/* position */
/* */
/********************************/
return 0;
}
int increment_name(int position)
/****************************************/
/* */
/* recursively revise the letters in */
/* a name to get the lexiographically */
/* next name */
/* */
/****************************************/
{
int next_position;
if (name[position] == 'z')
if (position == 0) {
fprintf(temp,
"ERROR: There are no more available names\n");
exit(-1);
} else {
name[position] = 'a'; /**********************/
next_position = --position; /* */
increment_name(next_position); /* increment the */
/* previous letter */
/* */
/**********************/
}
/*********************************/
/* */
else
name[position]++; /* otherwise, increment this one */
return 0; /* */
/*********************************/
}
int mode(char *path_string)
/****************************************/
/* */
/* determine and return the mode of */
/* the file named by path_string */
/* */
/****************************************/
{
struct stat buf;
int ret_val, mod;
ret_val = stat(path_string, &buf);
if (ret_val == -1)
return (-1);
else {
mod = buf.st_mode;
return (mod);
}
}
int escrivez(char *string)
{
char write_string[PATH_STRING_LENGTH + 1];
int len, ret_len;
strcpy(write_string, string);
len = strlen(write_string);
write_string[len] = '\n';
len++;
ret_len = write(list_id, write_string, len);
if (len != ret_len) {
fprintf(temp,
"A string of deviant length %d written to path_list, errno=%d\n",
ret_len, errno);
exit(-2);
}
return 0;
}
int term(void)
{
int status;
fflush(temp);
if (parent_pid == getpid()) {
massmurder(); /* kill kids */
fprintf(temp, "\term1 - SIGTERM received by parent.\n");
fflush(temp);
} else {
fprintf(temp, "\tchild - got SIGTERM signal.\n");
if (list_stream != NULL)
fclose(list_stream);
close(list_id);
close(file_id);
status = system(remove_string);
if (status) {
fprintf(temp, "Caution - ``%s'' returned status %d\n",
remove_string, status);
}
exit(0);
}
return 0;
}
int massmurder(void)
{
int i;
for (i = 0; i < MAXCHILD; i++) {
if (allchild[i]) {
kill(allchild[i], SIGTERM);
}
}
return 0;
}
/** LTP Port **/
/*
* setup
*
* Do set up - here its a dummy function
*/
void setup(void)
{
tst_tmpdir();
temp = stderr;
}
/*
* fail_exit()
*
* Exit on failure
*/
void fail_exit(void)
{
tst_brkm(TFAIL, tst_rmdir, "Test failed\n");
}
/*
*
* Function: anyfail()
*
* Description: Exit a test.
*/
void anyfail(void)
{
(local_flag == FAILED) ? tst_resm(TFAIL, "Test failed")
: tst_resm(TPASS, "Test passed");
tst_rmdir();
tst_exit();
}
/*
* ok_exit
*
* Calling block passed the test
*/
void ok_exit(void)
{
local_flag = PASSED;
return;
}
/*
* forkfail()
*
* exit on failure
*/
void forkfail(void)
{
tst_brkm(TBROK, tst_rmdir, "Reason: %s\n", strerror(errno));
}
/*
* Function: terror
*
* Description: prints error message this may not be because some part of the
* test case failed, for example fork() failed. We will log this
* failure as TBROK instead of TFAIL.
*/
void terror(char *message)
{
tst_resm(TBROK, "Reason: %s:%s\n", message, strerror(errno));
return;
}
/*
* instress
*
* Assume that we are always running under stress, so this function will
* return > 0 value always.
*/
int instress(void)
{
tst_resm(TINFO, "System resource may be too low, fork() malloc()"
" etc are likely to fail.\n");
return 1;
}