1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2002
4 * Copyright (c) Cyril Hrubis chrubis@suse.cz 2009
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
14 * the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*
22 * NAME
23 * ftest06.c -- test inode things (ported from SPIE section2/filesuite/ftest7.c, by Airong Zhang)
24 *
25 * this is the same as ftest2, except that it uses lseek64
26 *
27 * CALLS
28 * open, close, read, write, lseek64,
29 * unlink, chdir
30 *
31 *
32 * ALGORITHM
33 *
34 * This was tino.c by rbk. Moved to test suites by dale.
35 *
36 * ftest06 [-f tmpdirname] nchild iterations [partition]
37 *
38 * This forks some child processes, they do some random operations
39 * which use lots of directory operations.
40 *
41 * RESTRICTIONS
42 * Runs a long time with default args - can take others on input
43 * line. Use with "term mode".
44 * If run on vax the ftruncate will not be random - will always go to
45 * start of file. NOTE: produces a very high load average!!
46 *
47 */
48
49 #define _LARGEFILE64_SOURCE 1
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <fcntl.h>
54 #include <sys/stat.h>
55 #include <sys/mount.h>
56 #include <sys/wait.h>
57 #include <errno.h>
58 #include <signal.h>
59 #include <unistd.h>
60 #include "test.h"
61 #include "libftest.h"
62
63 char *TCID = "ftest06";
64 int TST_TOTAL = 1;
65
66 #define PASSED 1
67 #define FAILED 0
68
69 static void crfile(int, int);
70 static void unlfile(int, int);
71 static void fussdir(int, int);
72 static void dotest(int, int);
73 static void dowarn(int, char *, char *);
74 static void term(int sig);
75 static void cleanup(void);
76
77 #define MAXCHILD 25
78 #define K_1 1024
79 #define K_2 2048
80 #define K_4 4096
81
82 static int local_flag;
83
84 #define M (1024*1024)
85
86 static int iterations;
87 static int nchild;
88 static int parent_pid;
89 static int pidlist[MAXCHILD];
90
91 static char homedir[MAXPATHLEN];
92 static char dirname[MAXPATHLEN];
93 static int dirlen;
94 static int mnt = 0;
95 static char startdir[MAXPATHLEN], mntpoint[MAXPATHLEN];
96 static char *partition;
97 static char *cwd;
98 static char *fstyp;
99
main(int ac,char * av[])100 int main(int ac, char *av[])
101 {
102 int pid, child, status, count, k, j;
103 char name[MAXPATHLEN];
104
105 int lc;
106
107 /*
108 * parse standard options
109 */
110 tst_parse_opts(ac, av, NULL, NULL);
111
112 /*
113 * Default values for run conditions.
114 */
115 iterations = 50;
116 nchild = 5;
117
118 if (signal(SIGTERM, term) == SIG_ERR) {
119 tst_resm(TBROK, "first signal failed");
120
121 }
122
123 /* use the default values for run conditions */
124 for (lc = 0; TEST_LOOPING(lc); lc++) {
125
126 local_flag = PASSED;
127 /*
128 * Make a directory to do this in; ignore error if already exists.
129 */
130 parent_pid = getpid();
131 tst_tmpdir();
132
133 if (!startdir[0]) {
134 if (getcwd(startdir, MAXPATHLEN) == NULL) {
135 tst_brkm(TFAIL | TERRNO, NULL, "getcwd failed");
136 }
137 }
138 cwd = startdir;
139
140 snprintf(dirname, ARRAY_SIZE(dirname),
141 "%s/ftest06.%d", cwd, getpid());
142 snprintf(homedir, ARRAY_SIZE(homedir),
143 "%s/ftest06h.%d", cwd, getpid());
144
145 mkdir(dirname, 0755);
146 mkdir(homedir, 0755);
147
148 if (chdir(dirname) < 0)
149 tst_brkm(TFAIL | TERRNO, cleanup, "\tCan't chdir(%s)",
150 dirname);
151
152 dirlen = strlen(dirname);
153
154 if (chdir(homedir) < 0)
155 tst_brkm(TFAIL | TERRNO, cleanup, "\tCan't chdir(%s)",
156 homedir);
157
158 /* enter block */
159 for (k = 0; k < nchild; k++) {
160 if ((child = fork()) == 0) {
161 dotest(k, iterations);
162 tst_exit();
163 }
164 if (child < 0) {
165 tst_brkm(TBROK | TERRNO, cleanup,
166 "fork failed");
167 }
168 pidlist[k] = child;
169 }
170
171 /*
172 * Wait for children to finish.
173 */
174 count = 0;
175 while ((child = wait(&status)) > 0) {
176 //tst_resm(TINFO,"Test{%d} exited status = 0x%x", child, status);
177 //fprintf(stdout, "status is %d",status);
178 if (status) {
179 tst_resm(TFAIL,
180 "Test{%d} failed, expected 0 exit.",
181 child);
182 local_flag = FAILED;
183 }
184 ++count;
185 }
186
187 /*
188 * Should have collected all children.
189 */
190 if (count != nchild) {
191 tst_resm(TFAIL,
192 "Wrong # children waited on, count = %d",
193 count);
194 local_flag = FAILED;
195 }
196
197 if (local_flag == PASSED)
198 tst_resm(TPASS, "Test passed.");
199 else
200 tst_resm(TFAIL, "Test failed.");
201
202 if (iterations > 26)
203 iterations = 26;
204
205 for (k = 0; k < nchild; k++)
206 for (j = 0; j < iterations + 1; j++) {
207 ft_mkname(name, dirname, k, j);
208 rmdir(name);
209 unlink(name);
210 }
211
212 if (chdir(startdir) < 0)
213 tst_brkm(TFAIL | TERRNO, cleanup, "Can't chdir(%s)",
214 startdir);
215
216 pid = fork();
217 if (pid < 0) {
218 tst_brkm(TBROK | TERRNO, NULL, "fork failed");
219 }
220
221 if (pid == 0) {
222 execl("/bin/rm", "rm", "-rf", homedir, NULL);
223
224 } else
225 wait(&status);
226
227 if (status)
228 tst_resm(TINFO,
229 "CAUTION - ftest06, '%s' may not have been removed.",
230 homedir);
231
232 pid = fork();
233 if (pid < 0) {
234 tst_brkm(TBROK | TERRNO, NULL, "fork failed");
235 }
236 if (pid == 0) {
237 execl("/bin/rm", "rm", "-rf", dirname, NULL);
238 exit(1);
239 } else
240 wait(&status);
241 if (status) {
242 tst_resm(TWARN,
243 "CAUTION - ftest06, '%s' may not have been removed.",
244 dirname);
245 }
246
247 sync();
248
249 }
250
251 if (local_flag == FAILED)
252 tst_resm(TFAIL, "Test failed.");
253 else
254 tst_resm(TPASS, "Test passed.");
255
256 cleanup();
257 tst_exit();
258 }
259
260 #define warn(val,m1,m2) if ((val) < 0) dowarn(me,m1,m2)
261
262 /*
263 * crfile()
264 * Create a file and write something into it.
265 */
266 static char crmsg[] = "Gee, let's write something in the file!\n";
267
crfile(int me,int count)268 static void crfile(int me, int count)
269 {
270 int fd;
271 off64_t seekval;
272 int val;
273 char fname[MAXPATHLEN];
274 char buf[MAXPATHLEN];
275
276 ft_mkname(fname, dirname, me, count);
277
278 fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666);
279 if (fd < 0 && errno == EISDIR) {
280 val = rmdir(fname);
281 warn(val, "rmdir", fname);
282 fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666);
283 }
284 warn(fd, "creating", fname);
285
286 seekval = lseek64(fd, (off64_t) (rand() % M), 0);
287 warn(seekval, "lseek64", 0);
288
289 val = write(fd, crmsg, sizeof(crmsg) - 1);
290 warn(val, "write", 0);
291
292 seekval = lseek(fd, -((off64_t) sizeof(crmsg) - 1), 1);
293 warn(seekval, "lseek64", 0);
294
295 val = read(fd, buf, sizeof(crmsg) - 1);
296 warn(val, "read", 0);
297
298 if (strncmp(crmsg, buf, sizeof(crmsg) - 1))
299 dowarn(me, "compare", 0);
300
301 val = close(fd);
302 warn(val, "close", 0);
303 }
304
305 /*
306 * unlfile()
307 * Unlink some of the files.
308 */
unlfile(int me,int count)309 static void unlfile(int me, int count)
310 {
311 int val, i;
312 char fname[MAXPATHLEN];
313
314 i = count - 10;
315 if (i < 0)
316 i = 0;
317 for (; i < count; i++) {
318 ft_mkname(fname, dirname, me, i);
319 val = rmdir(fname);
320 if (val < 0)
321 val = unlink(fname);
322 if (val == 0 || errno == ENOENT)
323 continue;
324 dowarn(me, "unlink", fname);
325 }
326 }
327
328 /*
329 * fussdir()
330 * Make a directory, put stuff in it, remove it, and remove directory.
331 *
332 * Randomly leave the directory there.
333 */
fussdir(int me,int count)334 static void fussdir(int me, int count)
335 {
336 int val;
337 char dir[MAXPATHLEN], fname[MAXPATHLEN], savedir[MAXPATHLEN];
338
339 ft_mkname(dir, dirname, me, count);
340 rmdir(dir);
341 unlink(dir);
342
343 val = mkdir(dir, 0755);
344 warn(val, "mkdir", dir);
345
346 /*
347 * Arrange to create files in the directory.
348 */
349 strcpy(savedir, dirname);
350 strcpy(dirname, "");
351
352 val = chdir(dir);
353 warn(val, "chdir", dir);
354
355 crfile(me, count);
356 crfile(me, count + 1);
357
358 val = chdir("..");
359 warn(val, "chdir", "..");
360
361 val = rmdir(dir);
362
363 if (val >= 0) {
364 tst_brkm(TFAIL, NULL,
365 "Test[%d]: rmdir of non-empty %s succeeds!", me,
366 dir);
367 }
368
369 val = chdir(dir);
370 warn(val, "chdir", dir);
371
372 ft_mkname(fname, dirname, me, count);
373 val = unlink(fname);
374 warn(val, "unlink", fname);
375
376 ft_mkname(fname, dirname, me, count + 1);
377 val = unlink(fname);
378 warn(val, "unlink", fname);
379
380 val = chdir(homedir);
381 warn(val, "chdir", homedir);
382
383 if (rand() & 0x01) {
384 val = rmdir(dir);
385 warn(val, "rmdir", dir);
386 }
387
388 strcpy(dirname, savedir);
389 }
390
391 /*
392 * dotest()
393 * Children execute this.
394 *
395 * Randomly do an inode thing; loop for # iterations.
396 */
397 #define THING(p) {p, "p"}
398
399 struct ino_thing {
400 void (*it_proc) ();
401 char *it_name;
402 } ino_thing[] = {
403 THING(crfile), THING(unlfile), THING(fussdir), THING(sync),};
404
405 #define NTHING ARRAY_SIZE(ino_thing)
406
407 int thing_cnt[NTHING];
408 int thing_last[NTHING];
409
dotest(int me,int count)410 static void dotest(int me, int count)
411 {
412 int thing, i;
413
414 //tst_resm(TINFO,"Test %d pid %d starting.", me, getpid());
415
416 srand(getpid());
417
418 for (i = 0; i < count; i++) {
419 thing = (rand() >> 3) % NTHING;
420 (*ino_thing[thing].it_proc) (me, i, ino_thing[thing].it_name);
421 ++thing_cnt[thing];
422 }
423
424 //tst_resm(TINFO,"Test %d pid %d exiting.", me, getpid());
425 }
426
dowarn(int me,char * m1,char * m2)427 static void dowarn(int me, char *m1, char *m2)
428 {
429 int err = errno;
430
431 tst_brkm(TFAIL, NULL, "Test[%d]: error %d on %s %s",
432 me, err, m1, (m2 ? m2 : ""));
433 }
434
term(int sig LTP_ATTRIBUTE_UNUSED)435 static void term(int sig LTP_ATTRIBUTE_UNUSED)
436 {
437 int i;
438
439 tst_resm(TINFO, "\tterm -[%d]- got sig term.", getpid());
440
441 if (parent_pid == getpid()) {
442 for (i = 0; i < nchild; i++)
443 if (pidlist[i])
444 kill(pidlist[i], SIGTERM);
445 return;
446 }
447
448 tst_brkm(TBROK, NULL, "Term: Child process exiting.");
449 }
450
cleanup(void)451 static void cleanup(void)
452 {
453 char mount_buffer[1024];
454
455 if (mnt == 1) {
456 if (chdir(startdir) < 0) {
457 tst_resm(TINFO, "Could not change to %s ", startdir);
458 }
459 if (!strcmp(fstyp, "cfs")) {
460 sprintf(mount_buffer, "/bin/umount %s", partition);
461 if (system(mount_buffer) != 0) {
462 tst_resm(TINFO, "Unable to unmount %s from %s ",
463 partition, mntpoint);
464 if (umount(partition)) {
465 tst_resm(TINFO,
466 "Unable to unmount %s from %s ",
467 partition, mntpoint);
468 } else {
469 tst_resm(TINFO,
470 "Forced umount for %s, /etc/mtab now dirty",
471 partition);
472 }
473 }
474 } else {
475 if (umount(partition)) {
476 tst_resm(TINFO, "Unable to unmount %s from %s ",
477 partition, mntpoint);
478 }
479 }
480 if (rmdir(mntpoint) != 0) {
481 tst_resm(TINFO, "Unable to rmdir %s ", mntpoint);
482 }
483 }
484 tst_rmdir();
485
486 }
487