| /* |
| * |
| * 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 <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" |
| #include "usctest.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(argc, argv) |
| 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(str) |
| 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() |
| |
| /************************************************/ |
| /* */ |
| /* 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(); |
| |
| if (gen_ret_val > ch_ret_val) |
| exit_val = ch_ret_val; |
| else |
| exit_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(string, 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 */ |
| /* */ |
| /****************************************/ |
| /***************************/ |
| /* */ |
| char string[]; /* the directory path */ |
| /* string below which a */ |
| /* tree is generated */ |
| /* */ |
| /***************************/ |
| |
| /***************************/ |
| /* */ |
| int 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() |
| |
| /****************************************/ |
| /* */ |
| /* 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() |
| |
| /****************************************/ |
| /* */ |
| /* 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(position) |
| |
| /****************************************/ |
| /* */ |
| /* recursively revise the letters in */ |
| /* a name to get the lexiographically */ |
| /* next name */ |
| /* */ |
| /****************************************/ |
| int position; |
| { |
| 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(path_string) |
| |
| /****************************************/ |
| /* */ |
| /* determine and return the mode of */ |
| /* the file named by path_string */ |
| /* */ |
| /****************************************/ |
| char 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(string) |
| |
| 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() |
| { |
| 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() |
| { |
| 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() |
| { |
| tst_tmpdir(); |
| temp = stderr; |
| } |
| |
| /* |
| * fail_exit() |
| * |
| * Exit on failure |
| */ |
| void fail_exit() |
| { |
| tst_brkm(TFAIL, tst_rmdir, "Test failed\n"); |
| } |
| |
| /* |
| * |
| * Function: anyfail() |
| * |
| * Description: Exit a test. |
| */ |
| void anyfail() |
| { |
| (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() |
| { |
| local_flag = PASSED; |
| return; |
| } |
| |
| /* |
| * forkfail() |
| * |
| * exit on failure |
| */ |
| void forkfail() |
| { |
| 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() |
| { |
| tst_resm(TINFO, "System resource may be too low, fork() malloc()" |
| " etc are likely to fail.\n"); |
| return 1; |
| } |