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@us.ibm.com */
21 /* 06/30/2001 Port to Linux nsharoff@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
main(int argc,char * argv[])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
bd_arg(char * str)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
tree(void)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
generate(char * string,int level)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
check(void)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
get_next_name(void)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
increment_name(int position)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
mode(char * path_string)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
escrivez(char * string)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
term(void)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
massmurder(void)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 */
setup(void)827 void setup(void)
828 {
829 tst_tmpdir();
830 temp = stderr;
831 }
832
833 /*
834 * fail_exit()
835 *
836 * Exit on failure
837 */
fail_exit(void)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 */
anyfail(void)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 */
ok_exit(void)862 void ok_exit(void)
863 {
864 local_flag = PASSED;
865 return;
866 }
867
868 /*
869 * forkfail()
870 *
871 * exit on failure
872 */
forkfail(void)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 */
terror(char * message)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 */
instress(void)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