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 /* 11/01/2002 Port to LTP robbiew (at) us.ibm.com */ 21 /* 06/30/2001 Port to Linux nsharoff (at) us.ibm.com */ 22 23 /*inode02.c */ 24 /*====================================================================== 25 =================== TESTPLAN SEGMENT =================== 26 CALLS: mkdir, stat, open 27 28 Run with TERM mode. 29 30 >KEYS: < file system and I/O management, system resource constraints. 31 >WHAT: < Can the system handle a heavy load on the file system I/O 32 < functions? 33 >HOW: < Create several identical process that call inode02.c. This 34 < will simulate the multi-user environment, and hopefully uncover 35 < conflicts that might occur in "real life" use. 36 >BUGS: < 37 ======================================================================*/ 38 39 #define PATH_STRING_LENGTH 1024 40 #define NAME_LENGTH 8 41 #define MAX_PATH_STRING_LENGTH (PATH_STRING_LENGTH - NAME_LENGTH - 40) 42 #define DIRECTORY_MODE 00777 43 #define FILE_MODE 00777 44 45 #define MKDIR_STRING_LENGTH (MAX_PATH_STRING_LENGTH + 7) 46 47 /* #define DEBUG you can watch the generation with this flag */ 48 49 #define TRUE 1 50 #define FALSE 0 51 #define READ 0 52 #define WRITE 1 53 54 #include <stdio.h> 55 #include <errno.h> 56 #include <sys/types.h> 57 #include <sys/stat.h> 58 #include <signal.h> 59 #include <fcntl.h> 60 #include <errno.h> 61 #include <sys/wait.h> 62 63 #ifdef LINUX 64 #include <stdlib.h> 65 #include <unistd.h> 66 #include <string.h> 67 #endif 68 69 #define MAXCHILD 25 70 int allchild[MAXCHILD + 1]; 71 72 char name[NAME_LENGTH + 1]; 73 char path_string[PATH_STRING_LENGTH + 1]; 74 char read_string[PATH_STRING_LENGTH + 1]; 75 char write_string[PATH_STRING_LENGTH + 1]; 76 char remove_string[PATH_STRING_LENGTH + 10]; 77 int parent_pid; 78 int nchild; 79 80 FILE *list_stream = NULL; 81 int list_id; 82 int file_id; 83 84 int increment_name(), get_next_name(), mode(), escrivez(), massmurder(); 85 int max_depth, max_breadth, file_length; 86 int bd_arg(char *); 87 88 #ifdef LINUX 89 void (*sigset(int, void (*)(int))) (int); 90 #endif 91 92 /** LTP Port **/ 93 #include "test.h" 94 95 void setup(void); 96 void fail_exit(void); 97 void anyfail(void); 98 void ok_exit(void); 99 void forkfail(void); 100 void terror(char *); 101 int instress(void); 102 103 #define FAILED 0 104 #define PASSED 1 105 106 int local_flag = PASSED; 107 FILE *temp; 108 109 char *TCID = "inode02"; /* Test program identifier. */ 110 int TST_TOTAL = 1; /* Total number of test cases. */ 111 /**************/ 112 113 int main(int argc, char *argv[]) 114 { 115 int pid, tree(), p, status; 116 int count, child; 117 register int i; 118 int term(); 119 120 setup(); 121 122 parent_pid = getpid(); 123 124 if (sigset(SIGTERM, (void (*)())term) == SIG_ERR) { 125 tst_resm(TBROK, "\tSIGTERM sigset set failed, errno=%d\n", 126 errno); 127 exit(1); 128 } 129 130 /************************************************/ 131 /* */ 132 /* Input the parameters for the directory--- */ 133 /* file trees which are to be generated */ 134 /* */ 135 /************************************************/ 136 137 if (argc < 2) { 138 max_depth = 6; 139 max_breadth = 5; 140 file_length = 8; 141 nchild = 5; 142 } else if (argc < 5) { 143 tst_resm(TCONF, "Bad argument count.\n"); 144 printf 145 ("\tinode02 max_depth max_breadth file_length #children\n\tdefault: inode02 6 5 8 5\n"); 146 exit(1); 147 } else { 148 i = 1; 149 if (sscanf(argv[i++], "%d", &max_depth) != 1) 150 bd_arg(argv[i - 1]); 151 if (sscanf(argv[i++], "%d", &max_breadth) != 1) 152 bd_arg(argv[i - 1]); 153 if (sscanf(argv[i++], "%d", &file_length) != 1) 154 bd_arg(argv[i - 1]); 155 if (sscanf(argv[i++], "%d", &nchild) != 1) 156 bd_arg(argv[i - 1]); 157 if (nchild > MAXCHILD) { 158 fprintf(temp, "too many children - max is %d\n", 159 MAXCHILD); 160 exit(1); 161 } 162 } 163 164 /************************************************/ 165 /* */ 166 /* Generate and check nchild trees */ 167 /* */ 168 /************************************************/ 169 170 for (p = 0; p < nchild; p++) { 171 pid = fork(); 172 if (pid == 0) { 173 tree(); 174 } else { 175 if (pid < 1) { 176 terror 177 ("Fork failed (may be OK if under stress)"); 178 massmurder(); 179 if (instress()) { 180 ok_exit(); 181 } 182 forkfail(); 183 } 184 } 185 } 186 187 count = 0; 188 while ((child = wait(&status)) > 0) { 189 #ifdef DEBUG 190 tst_resm(TINFO, "Test %d exited status = %d\n", child, status); 191 #endif 192 if (status) { 193 fprintf(temp, "Test %d failed - expected 0 exit.\n", 194 child); 195 local_flag = FAILED; 196 } 197 count++; 198 } 199 200 if (count != nchild) { 201 tst_resm(TFAIL, "Wrong number of children waited on!\n"); 202 tst_resm(TFAIL, "Saw %d, expected %d\n", count, nchild); 203 local_flag = FAILED; 204 } 205 206 /************************************************/ 207 /* */ 208 /* And report the results.......... */ 209 /* */ 210 /************************************************/ 211 212 anyfail(); 213 /** NOT REACHED **/ 214 tst_exit(); 215 } 216 217 int bd_arg(char *str) 218 { 219 fprintf(temp, 220 "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", 221 str); 222 exit(1); 223 } 224 225 int tree(void) 226 227 /************************************************/ 228 /* */ 229 /* TREE */ 230 /* */ 231 /* generate a tree of directories and files */ 232 /* and save the path names in the path_list */ 233 /* file */ 234 /* */ 235 /* then, read the path names and attempt to */ 236 /* access the corresponding directories and */ 237 /* files */ 238 /* */ 239 /************************************************/ 240 { 241 int gen_ret_val, ch_ret_val, exit_val, level; 242 int ret_val; 243 int generate(), check(); 244 char path_list_string[PATH_STRING_LENGTH + 10]; 245 int len; 246 int status; 247 int snp_ret; 248 249 /********************************/ 250 /* */ 251 /* make the root directory for */ 252 /* the tree */ 253 /* */ 254 /********************************/ 255 256 sprintf(path_string, "inode02.%d", getpid()); 257 258 ret_val = mkdir(path_string, DIRECTORY_MODE); 259 260 if (ret_val == -1) { 261 tst_resm(TBROK, 262 "Reason: Impossible to create directory %s, errno=%d\n", 263 path_string, errno); 264 exit(-5); 265 } 266 267 strcpy(remove_string, "rm -rf "); 268 strcat(remove_string, path_string); 269 270 #ifdef DEBUG 271 tst_resm(TINFO, "\n%s\n", path_string); 272 #endif 273 274 /****************************************/ 275 /* */ 276 /* create the "path_list" file, in */ 277 /* which the list of generated paths */ 278 /* will be stored so that they later */ 279 /* may be checked */ 280 /* */ 281 /****************************************/ 282 283 snp_ret = snprintf(path_list_string, sizeof(path_list_string), 284 "%s/path_list", path_string); 285 if (snp_ret < 0 || snp_ret >= sizeof(path_list_string)) { 286 tst_resm(TBROK, "snprintf(path_list_string,..) returned %d", 287 snp_ret); 288 exit(-1); 289 } 290 list_id = creat(path_list_string, FILE_MODE); 291 if (list_id == -1) { 292 fprintf(temp, 293 "\nThe path_list file '%s' cannot be created, errno=%d\n", 294 path_list_string, errno); 295 exit(-7); 296 } 297 298 /****************************************/ 299 /* */ 300 /* and store its name in path_list */ 301 /* */ 302 /****************************************/ 303 304 strcpy(write_string, path_string); 305 len = strlen(write_string); 306 write_string[len++] = 'D'; 307 write_string[len] = '\0'; 308 escrivez(write_string); 309 310 /****************************************/ 311 /* */ 312 /* generate the directory-file tree */ 313 /* */ 314 /****************************************/ 315 316 level = 0; 317 318 #ifdef DEBUG 319 tst_resm(TINFO, "\n\t%s\n\n", "GENERATING:"); 320 #endif 321 322 gen_ret_val = generate(path_string, level); 323 close(list_id); 324 list_id = open(path_list_string, READ); 325 if (list_id == -1) { 326 fprintf(temp, 327 "\nThe path_list file cannot be opened for reading, errno=%d\n", 328 errno); 329 exit(-8); 330 } 331 list_stream = fdopen(list_id, "r"); 332 333 /****************************************/ 334 /* */ 335 /* check the directory-file tree */ 336 /* for correctness */ 337 /* */ 338 /****************************************/ 339 340 #ifdef DEBUG 341 tst_resm(TINFO, "\n\t%s\n\n", "CHECKING:"); 342 #endif 343 344 ch_ret_val = check(); 345 346 exit_val = MIN(ch_ret_val, gen_ret_val); 347 348 status = fclose(list_stream); 349 if (status != 0) { 350 fprintf(temp, 351 "Failed to close list_stream: ret=%d errno=%d (%s)\n", 352 status, errno, strerror(errno)); 353 exit(-8); 354 } 355 356 /* 357 * Remove file. 358 */ 359 360 status = system(remove_string); 361 if (status) { 362 fprintf(temp, "Caution - `%s' failed.\n", remove_string); 363 fprintf(temp, "Status returned %d.\n", status); 364 } 365 366 /****************************************/ 367 /* */ 368 /* .....and exit main */ 369 /* */ 370 /****************************************/ 371 372 exit(exit_val); 373 } 374 375 int generate(char *string, int level) 376 377 /****************************************/ 378 /* */ 379 /* generate recursively a tree of */ 380 /* directories and files: within */ 381 /* created directory, an alternating */ 382 /* series of files and directories */ 383 /* are constructed---until tree */ 384 /* breadth and depth limits are */ 385 /* reached or an error occurs */ 386 /* */ 387 /****************************************/ 388 /***************************/ 389 /* string: */ 390 /* the directory path */ 391 /* string below which a */ 392 /* tree is generated */ 393 /* */ 394 /***************************/ 395 396 /***************************/ 397 /* level: */ 398 /* the tree depth variable */ 399 /* */ 400 /***************************/ 401 { 402 int switch_flag; 403 int ret_val = 0; 404 int new_ret_val, len, ret_len; 405 char new_string[PATH_STRING_LENGTH + 1]; 406 int new_level; 407 int i, j; /* iteration counters */ 408 int snp_ret; 409 410 switch_flag = level & TRUE; 411 if (strlen(string) >= MAX_PATH_STRING_LENGTH) { 412 413 /********************************/ 414 /* */ 415 /* Maximum path name length */ 416 /* reached */ 417 /* */ 418 /********************************/ 419 420 fprintf(temp, "\nMaximum path_name length reached\n"); 421 return (-1); 422 } else if (level < max_depth) { 423 for (i = 0; i <= max_breadth; i++) { 424 get_next_name(); 425 snp_ret = snprintf(new_string, sizeof(new_string), 426 "%s/%s", string, name); 427 if (snp_ret < 0 || snp_ret >= sizeof(new_string)) { 428 tst_resm(TBROK, "snprintf(new_string,..) " 429 "returned %d", snp_ret); 430 exit(-1); 431 } 432 433 /****************************************/ 434 /* */ 435 /* switch between creating files */ 436 /* and making directories */ 437 /* */ 438 /****************************************/ 439 440 if (switch_flag) { 441 switch_flag = FALSE; 442 443 /****************************************/ 444 /* */ 445 /* create a new file */ 446 /* */ 447 /****************************************/ 448 449 file_id = creat(new_string, FILE_MODE); 450 if (file_id == -1) { 451 fprintf(temp, 452 "\nImpossible to create file %s, errno=%d\n", 453 new_string, errno); 454 return (-2); 455 } 456 #ifdef DEBUG 457 tst_resm(TINFO, "%d %s F\n", level, 458 new_string); 459 #endif 460 461 /****************************************/ 462 /* */ 463 /* write to it */ 464 /* */ 465 /****************************************/ 466 467 len = strlen(new_string); 468 for (j = 1; j <= file_length; j++) { 469 ret_len = 470 write(file_id, new_string, len); 471 if (ret_len != len) { 472 fprintf(temp, 473 "\nUnsuccessful write to file %s, errno=%d\n", 474 new_string, errno); 475 return (-3); 476 } 477 } 478 close(file_id); 479 480 /****************************************/ 481 /* */ 482 /* and store its name in path_list */ 483 /* */ 484 /****************************************/ 485 486 strcpy(write_string, new_string); 487 len = strlen(write_string); 488 write_string[len++] = 'F'; 489 write_string[len] = '\0'; 490 escrivez(write_string); 491 } else { 492 switch_flag = TRUE; 493 494 /****************************************/ 495 /* */ 496 /* or make a directory */ 497 /* */ 498 /* (mknod can only be called when in */ 499 /* super user mode) */ 500 /* */ 501 /****************************************/ 502 503 ret_val = mkdir(new_string, DIRECTORY_MODE); 504 505 if (ret_val != 0) { 506 fprintf(temp, 507 "\nImpossible to create directory %s, errno=%d\n", 508 new_string, errno); 509 return (-5); 510 } 511 #ifdef DEBUG 512 tst_resm(TINFO, "%d %s D\n", level, 513 new_string); 514 #endif 515 516 /****************************************/ 517 /* */ 518 /* store its name in path_list */ 519 /* */ 520 /****************************************/ 521 522 strcpy(write_string, new_string); 523 len = strlen(write_string); 524 write_string[len++] = 'D'; 525 write_string[len] = '\0'; 526 escrivez(write_string); 527 528 /****************************************/ 529 /* */ 530 /* and generate a new level */ 531 /* */ 532 /****************************************/ 533 534 new_level = level + 1; 535 new_ret_val = generate(new_string, new_level); 536 if (new_ret_val < ret_val) 537 ret_val = new_ret_val; 538 } 539 } 540 541 /********************************/ 542 /* */ 543 /* Maximum breadth reached */ 544 /* */ 545 /********************************/ 546 547 return (ret_val); 548 } else 549 /********************************/ 550 /* */ 551 /* Maximum depth reached */ 552 /* */ 553 /********************************/ 554 return 0; 555 } 556 557 int check(void) 558 559 /****************************************/ 560 /* */ 561 /* check for file and directory */ 562 /* correctness by reading records */ 563 /* from the path_list and attempting */ 564 /* to determine if the corresponding */ 565 /* files or directories are as */ 566 /* created */ 567 /* */ 568 /****************************************/ 569 { 570 int len, path_mode, val, ret_len, j; 571 572 for (;;) { 573 574 /****************************************/ 575 /* */ 576 /* read a path string from path_list */ 577 /* */ 578 /****************************************/ 579 580 if (fscanf(list_stream, "%s", path_string) == EOF) { 581 582 #ifdef DEBUG 583 tst_resm(TINFO, "\nEnd of path_list file reached \n"); 584 #endif 585 586 return 0; 587 } 588 #ifdef DEBUG 589 tst_resm(TINFO, "%s\n", path_string); 590 #endif 591 592 len = strlen(path_string); 593 len--; 594 if (path_string[len] == 'F') { 595 596 /********************************/ 597 /* */ 598 /* this should be a file */ 599 /* */ 600 /********************************/ 601 602 path_string[len] = '\0'; 603 file_id = open(path_string, READ); 604 if (file_id <= 0) { 605 fprintf(temp, 606 "\nImpossible to open file %s, errno=%d\n", 607 path_string, errno); 608 return (-1); 609 } 610 611 else { 612 /********************************/ 613 /* */ 614 /* check its contents */ 615 /* */ 616 /********************************/ 617 618 ret_len = 0; 619 len = strlen(path_string); 620 for (j = 1; j <= file_length; j++) { 621 ret_len = 622 read(file_id, read_string, len); 623 if (len != ret_len) { 624 fprintf(temp, 625 "\nFile read error for file %s, errno=%d\n", 626 path_string, errno); 627 return (-3); 628 } 629 read_string[len] = '\0'; 630 val = strcmp(read_string, path_string); 631 if (val != 0) { 632 fprintf(temp, 633 "\nContents of file %s are different than expected: %s\n", 634 path_string, 635 read_string); 636 return (-4); 637 } 638 } 639 close(file_id); 640 } /* else for */ 641 if (ret_len <= 0) { 642 fprintf(temp, 643 "\nImpossible to read file %s, errno=%d\n", 644 path_string, errno); 645 return (-2); 646 } 647 } else { 648 649 /********************************/ 650 /* */ 651 /* otherwise.......... */ 652 /* it should be a directory */ 653 /* */ 654 /********************************/ 655 656 path_string[len] = '\0'; 657 path_mode = mode(path_string); 658 if (path_mode == -1) { 659 fprintf(temp, 660 "\nPreviously created directory path %s was not open\n", 661 path_string); 662 return (-4); 663 } 664 if ((040000 & path_mode) != 040000) { 665 fprintf(temp, 666 "\nPath %s was not recognized to be a directory\n", 667 path_string); 668 fprintf(temp, "Its mode is %o\n", path_mode); 669 return (-5); 670 } 671 } 672 } /* while */ 673 } 674 675 int get_next_name(void) 676 677 /****************************************/ 678 /* */ 679 /* get the next---in a dictionary */ 680 /* sense---file or directory name */ 681 /* */ 682 /****************************************/ 683 { 684 static int k; 685 int i; 686 int last_position; 687 688 last_position = NAME_LENGTH - 1; 689 if (k == 0) { 690 691 /************************/ 692 /* */ 693 /* initialize name */ 694 /* */ 695 /************************/ 696 697 for (i = 0; i < NAME_LENGTH; i++) 698 name[i] = 'a'; 699 name[NAME_LENGTH] = '\0'; 700 k++; 701 } 702 /********************************/ 703 /* */ 704 else 705 increment_name(last_position); /* i.e., beginning at the last */ 706 /* position */ 707 /* */ 708 /********************************/ 709 return 0; 710 } 711 712 int increment_name(int position) 713 714 /****************************************/ 715 /* */ 716 /* recursively revise the letters in */ 717 /* a name to get the lexiographically */ 718 /* next name */ 719 /* */ 720 /****************************************/ 721 { 722 int next_position; 723 724 if (name[position] == 'z') 725 if (position == 0) { 726 fprintf(temp, 727 "ERROR: There are no more available names\n"); 728 exit(-1); 729 } else { 730 name[position] = 'a'; /**********************/ 731 next_position = --position; /* */ 732 increment_name(next_position); /* increment the */ 733 /* previous letter */ 734 /* */ 735 /**********************/ 736 } 737 /*********************************/ 738 /* */ 739 else 740 name[position]++; /* otherwise, increment this one */ 741 return 0; /* */ 742 /*********************************/ 743 } 744 745 int mode(char *path_string) 746 747 /****************************************/ 748 /* */ 749 /* determine and return the mode of */ 750 /* the file named by path_string */ 751 /* */ 752 /****************************************/ 753 { 754 struct stat buf; 755 int ret_val, mod; 756 757 ret_val = stat(path_string, &buf); 758 if (ret_val == -1) 759 return (-1); 760 else { 761 mod = buf.st_mode; 762 return (mod); 763 } 764 } 765 766 int escrivez(char *string) 767 { 768 char write_string[PATH_STRING_LENGTH + 1]; 769 int len, ret_len; 770 771 strcpy(write_string, string); 772 len = strlen(write_string); 773 write_string[len] = '\n'; 774 len++; 775 ret_len = write(list_id, write_string, len); 776 if (len != ret_len) { 777 fprintf(temp, 778 "A string of deviant length %d written to path_list, errno=%d\n", 779 ret_len, errno); 780 exit(-2); 781 } 782 return 0; 783 } 784 785 int term(void) 786 { 787 int status; 788 789 fflush(temp); 790 if (parent_pid == getpid()) { 791 massmurder(); /* kill kids */ 792 fprintf(temp, "\term1 - SIGTERM received by parent.\n"); 793 fflush(temp); 794 } else { 795 fprintf(temp, "\tchild - got SIGTERM signal.\n"); 796 if (list_stream != NULL) 797 fclose(list_stream); 798 close(list_id); 799 close(file_id); 800 status = system(remove_string); 801 if (status) { 802 fprintf(temp, "Caution - ``%s'' returned status %d\n", 803 remove_string, status); 804 } 805 exit(0); 806 } 807 return 0; 808 } 809 810 int massmurder(void) 811 { 812 int i; 813 for (i = 0; i < MAXCHILD; i++) { 814 if (allchild[i]) { 815 kill(allchild[i], SIGTERM); 816 } 817 } 818 return 0; 819 } 820 821 /** LTP Port **/ 822 /* 823 * setup 824 * 825 * Do set up - here its a dummy function 826 */ 827 void setup(void) 828 { 829 tst_tmpdir(); 830 temp = stderr; 831 } 832 833 /* 834 * fail_exit() 835 * 836 * Exit on failure 837 */ 838 void fail_exit(void) 839 { 840 tst_brkm(TFAIL, tst_rmdir, "Test failed\n"); 841 } 842 843 /* 844 * 845 * Function: anyfail() 846 * 847 * Description: Exit a test. 848 */ 849 void anyfail(void) 850 { 851 (local_flag == FAILED) ? tst_resm(TFAIL, "Test failed") 852 : tst_resm(TPASS, "Test passed"); 853 tst_rmdir(); 854 tst_exit(); 855 } 856 857 /* 858 * ok_exit 859 * 860 * Calling block passed the test 861 */ 862 void ok_exit(void) 863 { 864 local_flag = PASSED; 865 return; 866 } 867 868 /* 869 * forkfail() 870 * 871 * exit on failure 872 */ 873 void forkfail(void) 874 { 875 tst_brkm(TBROK, tst_rmdir, "Reason: %s\n", strerror(errno)); 876 } 877 878 /* 879 * Function: terror 880 * 881 * Description: prints error message this may not be because some part of the 882 * test case failed, for example fork() failed. We will log this 883 * failure as TBROK instead of TFAIL. 884 */ 885 void terror(char *message) 886 { 887 tst_resm(TBROK, "Reason: %s:%s\n", message, strerror(errno)); 888 return; 889 } 890 891 /* 892 * instress 893 * 894 * Assume that we are always running under stress, so this function will 895 * return > 0 value always. 896 */ 897 int instress(void) 898 { 899 tst_resm(TINFO, "System resource may be too low, fork() malloc()" 900 " etc are likely to fail.\n"); 901 return 1; 902 } 903