1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
4 * Copyright (c) International Business Machines Corp., 2001
5 *
6 * This program is designed to stress the NFS implimentation. Many bugs were
7 * uncovered in the AIX operating system implimentation of NFS when AIX kernel
8 * was built over NFS. Source directory on a remote machine (one server many
9 * clients) NFS-mounted on to a directory on a local machine from which the
10 * kernel build was initiated. Apparently many defects/bugs were uncovered when
11 * multiple users tried to build the kernel by NFS mounting the kernel source
12 * from a remote machine and tried to build the kernel on a local machine.
13 *
14 * The test's aimed to stress NFS client/server and recreates such a senario.
15 * Spawn N number of threads. Each thread does the following:
16 * * create a directory tree;
17 * * populate it with ".c" files and makefiles;
18 * hostname.1234
19 * | - 1234.0.0.c
20 * | - ..........
21 * | - makefile
22 * |_ 1234.0
23 * |
24 * | - 1234.1.0.c
25 * | - ..........
26 * | - makefile
27 * |_ 1234.1
28 * |....
29 *
30 * * initate a build, executable will print hello world;
31 * * clean up all the executables that were created;
32 * * recurssively remove each subdir and its contents.
33 */
34
35 #define _GNU_SOURCE
36 #include <stdio.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <pthread.h>
44 #include <sys/mount.h>
45 #include <linux/limits.h>
46 #include <errno.h>
47 #include <linux/unistd.h>
48
49 #include "lapi/mkdirat.h"
50 #include "tst_safe_pthread.h"
51 #include "tst_safe_stdio.h"
52 #include "tst_test.h"
53
54 #define gettid() syscall(__NR_gettid)
55
56 static int thrd_num = 8;
57 static int dirs_num = 100;
58 static int file_num = 100;
59
60 static char *t_arg, *d_arg, *f_arg;
61
62 static struct tst_option opts[] = {
63 {"t:", &t_arg, "-t x Number of threads to generate, default: 8\n"},
64 {"d:", &d_arg, "-d x Number of subdirs to generate, default: 100\n"},
65 {"f:", &f_arg, "-f x Number of c files in each dir, default: 100\n"},
66 {NULL, NULL, NULL}
67 };
68
run_targets(const char * dirname,char * cfile,pid_t tid)69 static void run_targets(const char *dirname, char *cfile, pid_t tid)
70 {
71 int i, k, fd;
72 char subdir[PATH_MAX] = {0};
73 char *output_file;
74 char buf[11];
75 const char *const cmd_run[] = {cfile, NULL};
76
77 SAFE_ASPRINTF(&output_file, "%s/cmd.out", dirname);
78
79 /* run each binary */
80 for (i = 0; i < dirs_num; ++i) {
81 for (k = 0; k < file_num; ++k) {
82 snprintf(cfile, PATH_MAX, "%s%s/%d.%d.%d",
83 dirname, subdir, tid, i, k);
84
85 tst_run_cmd(cmd_run, output_file, NULL, 0);
86
87 fd = SAFE_OPEN(output_file, O_RDONLY);
88 SAFE_READ(1, fd, buf, 11);
89 if (strncmp(buf, "hello world", 11))
90 tst_brk(TFAIL, "command printed wrong message");
91 SAFE_CLOSE(fd);
92 }
93 strcat(subdir, "/dir");
94 }
95
96 free(output_file);
97 }
98
thread_fn(LTP_ATTRIBUTE_UNUSED void * args)99 static void *thread_fn(LTP_ATTRIBUTE_UNUSED void *args)
100 {
101 const char prog_buf[] = "#include <stdio.h>\n"
102 "int main(void)\n{\n"
103 "\tprintf(\"hello world\");\n"
104 "\treturn 0;\n}\n";
105
106 const char make_buf_n[] = "CFLAGS := -O -w -g\n"
107 "SRCS=$(wildcard *.c)\n"
108 "TARGETS=$(SRCS:.c=)\n"
109 "all: $(TARGETS)\n"
110 "$(TARGETS): %: %.c\n"
111 "\t$(CC) -o $@ $<\n"
112 "clean:\n\trm -f $(TARGETS)\n"
113 ".PHONY: all clean\n";
114
115 const char make_buf[] = "CFLAGS := -O -w -g\n"
116 "SUBDIR = dir\n"
117 "SRCS=$(wildcard *.c)\n"
118 "TARGETS=$(SRCS:.c=)\n"
119 "all: $(SUBDIR) $(TARGETS)\n"
120 "$(TARGETS): %: %.c\n"
121 "\t$(CC) -o $@ $<\n"
122 "$(SUBDIR):\n\t$(MAKE) -C $@\n"
123 "clean:\n"
124 "\trm -f $(TARGETS)\n"
125 "\t$(MAKE) -C $(SUBDIR) clean\n"
126 ".PHONY: all $(SUBDIR) clean\n";
127
128 int i, k, fd, dirfd, ret;
129 char *dirname;
130 char cfile[PATH_MAX];
131 char hostname[256];
132 pid_t tid = gettid();
133
134 SAFE_GETHOSTNAME(hostname, 256);
135 SAFE_ASPRINTF(&dirname, "%s.%ld", hostname, tid);
136
137 SAFE_MKDIR(dirname, 0755);
138 dirfd = SAFE_OPEN(dirname, O_DIRECTORY);
139
140 for (i = 0; i < dirs_num; ++i) {
141
142 fd = openat(dirfd, "makefile", O_CREAT | O_RDWR,
143 S_IRWXU | S_IRWXG | S_IRWXO);
144 if (fd < 0)
145 tst_brk(TFAIL | TERRNO, "openat(makefile) failed");
146
147 if (i == dirs_num - 1)
148 SAFE_WRITE(1, fd, make_buf_n, sizeof(make_buf_n) - 1);
149 else
150 SAFE_WRITE(1, fd, make_buf, sizeof(make_buf) - 1);
151
152 SAFE_CLOSE(fd);
153
154 for (k = 0; k < file_num; ++k) {
155 snprintf(cfile, PATH_MAX, "%d.%d.%d.c", tid, i, k);
156 fd = openat(dirfd, cfile, O_CREAT | O_RDWR,
157 S_IRWXU | S_IRWXG | S_IRWXO);
158 if (fd < 0) {
159 tst_brk(TFAIL | TERRNO,
160 "openat(%s) failed", cfile);
161 }
162
163 SAFE_WRITE(1, fd, prog_buf, sizeof(prog_buf) - 1);
164 SAFE_CLOSE(fd);
165 }
166
167 if (i == dirs_num - 1)
168 break;
169
170 ret = mkdirat(dirfd, "dir", 0755);
171 if (ret < 0)
172 tst_brk(TFAIL | TERRNO, "mkdirat('dir') failed");
173 dirfd = openat(dirfd, "dir", O_DIRECTORY);
174 if (dirfd < 0)
175 tst_brk(TFAIL | TERRNO, "openat('dir') failed");
176 }
177
178 const char *const cmd_make[] = {"make", "-s", "-C", dirname, NULL};
179 const char *const cmd_make_clean[] = {
180 "make", "-C", dirname, "-s", "clean", NULL};
181
182 tst_run_cmd(cmd_make, NULL, NULL, 0);
183
184 run_targets(dirname, cfile, tid);
185
186 tst_run_cmd(cmd_make_clean, NULL, NULL, 0);
187
188 free(dirname);
189
190 return NULL;
191 }
192
setup(void)193 static void setup(void)
194 {
195 thrd_num = atoi(t_arg);
196 dirs_num = atoi(d_arg);
197 file_num = atoi(f_arg);
198 }
199
do_test(void)200 static void do_test(void)
201 {
202 int i;
203 pthread_t id[thrd_num];
204
205 for (i = 0; i < thrd_num; ++i)
206 SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, NULL);
207
208 for (i = 0; i < thrd_num; ++i)
209 SAFE_PTHREAD_JOIN(id[i], NULL);
210
211 tst_res(TPASS, "'make' successfully build and clean all targets");
212 }
213
214 static struct tst_test test = {
215 .options = opts,
216 .test_all = do_test,
217 .setup = setup,
218 };
219