| /* |
| * |
| * Copyright (c) International Business Machines Corp., 2002 |
| * Copyright (c) Cyril Hrubis chrubis@suse.cz 2009 |
| * |
| * 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 |
| */ |
| |
| /* |
| * NAME |
| * ftest06.c -- test inode things (ported from SPIE section2/filesuite/ftest7.c, by Airong Zhang) |
| * |
| * this is the same as ftest2, except that it uses lseek64 |
| * |
| * CALLS |
| * open, close, read, write, llseek, |
| * unlink, chdir |
| * |
| * |
| * ALGORITHM |
| * |
| * This was tino.c by rbk. Moved to test suites by dale. |
| * |
| * ftest06 [-f tmpdirname] nchild iterations [partition] |
| * |
| * This forks some child processes, they do some random operations |
| * which use lots of directory operations. |
| * |
| * RESTRICTIONS |
| * Runs a long time with default args - can take others on input |
| * line. Use with "term mode". |
| * If run on vax the ftruncate will not be random - will always go to |
| * start of file. NOTE: produces a very high load average!! |
| * |
| */ |
| |
| #define _LARGEFILE64_SOURCE 1 |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/mount.h> |
| #include <sys/wait.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include "test.h" |
| #include "usctest.h" |
| #include "libftest.h" |
| |
| char *TCID = "ftest06"; |
| int TST_TOTAL = 1; |
| |
| #define PASSED 1 |
| #define FAILED 0 |
| |
| static void crfile(int, int); |
| static void unlfile(int, int); |
| static void fussdir(int, int); |
| static void dotest(int, int); |
| static void dowarn(int, char *, char *); |
| static void term(int sig); |
| static void cleanup(void); |
| |
| #define MAXCHILD 25 |
| #define K_1 1024 |
| #define K_2 2048 |
| #define K_4 4096 |
| |
| static int local_flag; |
| |
| #define M (1024*1024) |
| |
| static int iterations; |
| static int nchild; |
| static int parent_pid; |
| static int pidlist[MAXCHILD]; |
| |
| static char homedir[MAXPATHLEN]; |
| static char dirname[MAXPATHLEN]; |
| static int dirlen; |
| static int mnt = 0; |
| static char startdir[MAXPATHLEN], mntpoint[MAXPATHLEN]; |
| static char *partition; |
| static char *cwd; |
| static char *fstyp; |
| |
| int main(int ac, char *av[]) |
| { |
| int pid, child, status, count, k, j; |
| char name[3]; |
| |
| int lc; |
| const char *msg; |
| |
| /* |
| * parse standard options |
| */ |
| if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| /* |
| * Default values for run conditions. |
| */ |
| iterations = 50; |
| nchild = 5; |
| |
| if (signal(SIGTERM, term) == SIG_ERR) { |
| tst_resm(TBROK, "first signal failed"); |
| |
| } |
| |
| /* use the default values for run conditions */ |
| for (lc = 0; TEST_LOOPING(lc); lc++) { |
| |
| local_flag = PASSED; |
| /* |
| * Make a directory to do this in; ignore error if already exists. |
| */ |
| parent_pid = getpid(); |
| tst_tmpdir(); |
| |
| if (!startdir[0]) { |
| if (getcwd(startdir, MAXPATHLEN) == NULL) { |
| tst_brkm(TFAIL | TERRNO, NULL, "getcwd failed"); |
| } |
| } |
| cwd = startdir; |
| |
| snprintf(dirname, ARRAY_SIZE(dirname), |
| "%s/ftest06.%d", cwd, getpid()); |
| snprintf(homedir, ARRAY_SIZE(homedir), |
| "%s/ftest06h.%d", cwd, getpid()); |
| |
| mkdir(dirname, 0755); |
| mkdir(homedir, 0755); |
| |
| if (chdir(dirname) < 0) |
| tst_brkm(TFAIL | TERRNO, cleanup, "\tCan't chdir(%s)", |
| dirname); |
| |
| dirlen = strlen(dirname); |
| |
| if (chdir(homedir) < 0) |
| tst_brkm(TFAIL | TERRNO, cleanup, "\tCan't chdir(%s)", |
| homedir); |
| |
| /* enter block */ |
| for (k = 0; k < nchild; k++) { |
| if ((child = fork()) == 0) { |
| dotest(k, iterations); |
| tst_exit(); |
| } |
| if (child < 0) { |
| tst_brkm(TBROK | TERRNO, cleanup, |
| "fork failed"); |
| } |
| pidlist[k] = child; |
| } |
| |
| /* |
| * Wait for children to finish. |
| */ |
| count = 0; |
| while ((child = wait(&status)) > 0) { |
| //tst_resm(TINFO,"Test{%d} exited status = 0x%x", child, status); |
| //fprintf(stdout, "status is %d",status); |
| if (status) { |
| tst_resm(TFAIL, |
| "Test{%d} failed, expected 0 exit.", |
| child); |
| local_flag = FAILED; |
| } |
| ++count; |
| } |
| |
| /* |
| * Should have collected all children. |
| */ |
| if (count != nchild) { |
| tst_resm(TFAIL, |
| "Wrong # children waited on, count = %d", |
| count); |
| local_flag = FAILED; |
| } |
| |
| if (local_flag == PASSED) |
| tst_resm(TPASS, "Test passed."); |
| else |
| tst_resm(TFAIL, "Test failed."); |
| |
| if (iterations > 26) |
| iterations = 26; |
| |
| for (k = 0; k < nchild; k++) |
| for (j = 0; j < iterations + 1; j++) { |
| ft_mkname(name, dirname, k, j); |
| rmdir(name); |
| unlink(name); |
| } |
| |
| if (chdir(startdir) < 0) |
| tst_brkm(TFAIL | TERRNO, cleanup, "Can't chdir(%s)", |
| startdir); |
| |
| pid = fork(); |
| if (pid < 0) { |
| tst_brkm(TBROK | TERRNO, NULL, "fork failed"); |
| } |
| |
| if (pid == 0) { |
| execl("/bin/rm", "rm", "-rf", homedir, NULL); |
| |
| } else |
| wait(&status); |
| |
| if (status) |
| tst_resm(TINFO, |
| "CAUTION - ftest06, '%s' may not have been removed.", |
| homedir); |
| |
| pid = fork(); |
| if (pid < 0) { |
| tst_brkm(TBROK | TERRNO, NULL, "fork failed"); |
| } |
| if (pid == 0) { |
| execl("/bin/rm", "rm", "-rf", dirname, NULL); |
| exit(1); |
| } else |
| wait(&status); |
| if (status) { |
| tst_resm(TWARN, |
| "CAUTION - ftest06, '%s' may not have been removed.", |
| dirname); |
| } |
| |
| sync(); |
| |
| } |
| |
| if (local_flag == FAILED) |
| tst_resm(TFAIL, "Test failed."); |
| else |
| tst_resm(TPASS, "Test passed."); |
| |
| cleanup(); |
| tst_exit(); |
| } |
| |
| #define warn(val,m1,m2) if ((val) < 0) dowarn(me,m1,m2) |
| |
| /* |
| * crfile() |
| * Create a file and write something into it. |
| */ |
| static char crmsg[] = "Gee, let's write something in the file!\n"; |
| |
| static void crfile(int me, int count) |
| { |
| int fd; |
| off64_t seekval; |
| int val; |
| char fname[MAXPATHLEN]; |
| char buf[MAXPATHLEN]; |
| |
| ft_mkname(fname, dirname, me, count); |
| |
| fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666); |
| if (fd < 0 && errno == EISDIR) { |
| val = rmdir(fname); |
| warn(val, "rmdir", fname); |
| fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666); |
| } |
| warn(fd, "creating", fname); |
| |
| seekval = lseek64(fd, (off64_t) (rand() % M), 0); |
| warn(seekval, "lseek64", 0); |
| |
| val = write(fd, crmsg, sizeof(crmsg) - 1); |
| warn(val, "write", 0); |
| |
| seekval = lseek(fd, -((off64_t) sizeof(crmsg) - 1), 1); |
| warn(seekval, "lseek64", 0); |
| |
| val = read(fd, buf, sizeof(crmsg) - 1); |
| warn(val, "read", 0); |
| |
| if (strncmp(crmsg, buf, sizeof(crmsg) - 1)) |
| dowarn(me, "compare", 0); |
| |
| val = close(fd); |
| warn(val, "close", 0); |
| } |
| |
| /* |
| * unlfile() |
| * Unlink some of the files. |
| */ |
| static void unlfile(int me, int count) |
| { |
| int val, i; |
| char fname[MAXPATHLEN]; |
| |
| i = count - 10; |
| if (i < 0) |
| i = 0; |
| for (; i < count; i++) { |
| ft_mkname(fname, dirname, me, i); |
| val = rmdir(fname); |
| if (val < 0) |
| val = unlink(fname); |
| if (val == 0 || errno == ENOENT) |
| continue; |
| dowarn(me, "unlink", fname); |
| } |
| } |
| |
| /* |
| * fussdir() |
| * Make a directory, put stuff in it, remove it, and remove directory. |
| * |
| * Randomly leave the directory there. |
| */ |
| static void fussdir(int me, int count) |
| { |
| int val; |
| char dir[MAXPATHLEN], fname[MAXPATHLEN], savedir[MAXPATHLEN]; |
| |
| ft_mkname(dir, dirname, me, count); |
| rmdir(dir); |
| unlink(dir); |
| |
| val = mkdir(dir, 0755); |
| warn(val, "mkdir", dir); |
| |
| /* |
| * Arrange to create files in the directory. |
| */ |
| strcpy(savedir, dirname); |
| strcpy(dirname, ""); |
| |
| val = chdir(dir); |
| warn(val, "chdir", dir); |
| |
| crfile(me, count); |
| crfile(me, count + 1); |
| |
| val = chdir(".."); |
| warn(val, "chdir", ".."); |
| |
| val = rmdir(dir); |
| |
| if (val >= 0) { |
| tst_brkm(TFAIL, NULL, |
| "Test[%d]: rmdir of non-empty %s succeeds!", me, |
| dir); |
| } |
| |
| val = chdir(dir); |
| warn(val, "chdir", dir); |
| |
| ft_mkname(fname, dirname, me, count); |
| val = unlink(fname); |
| warn(val, "unlink", fname); |
| |
| ft_mkname(fname, dirname, me, count + 1); |
| val = unlink(fname); |
| warn(val, "unlink", fname); |
| |
| val = chdir(homedir); |
| warn(val, "chdir", homedir); |
| |
| if (rand() & 0x01) { |
| val = rmdir(dir); |
| warn(val, "rmdir", dir); |
| } |
| |
| strcpy(dirname, savedir); |
| } |
| |
| /* |
| * dotest() |
| * Children execute this. |
| * |
| * Randomly do an inode thing; loop for # iterations. |
| */ |
| #define THING(p) {p, "p"} |
| |
| struct ino_thing { |
| void (*it_proc) (); |
| char *it_name; |
| } ino_thing[] = { |
| THING(crfile), THING(unlfile), THING(fussdir), THING(sync),}; |
| |
| #define NTHING (sizeof(ino_thing) / sizeof(ino_thing[0])) |
| |
| int thing_cnt[NTHING]; |
| int thing_last[NTHING]; |
| |
| static void dotest(int me, int count) |
| { |
| int thing, i; |
| |
| //tst_resm(TINFO,"Test %d pid %d starting.", me, getpid()); |
| |
| srand(getpid()); |
| |
| for (i = 0; i < count; i++) { |
| thing = (rand() >> 3) % NTHING; |
| (*ino_thing[thing].it_proc) (me, i, ino_thing[thing].it_name); |
| ++thing_cnt[thing]; |
| } |
| |
| //tst_resm(TINFO,"Test %d pid %d exiting.", me, getpid()); |
| } |
| |
| static void dowarn(int me, char *m1, char *m2) |
| { |
| int err = errno; |
| |
| tst_brkm(TFAIL, NULL, "Test[%d]: error %d on %s %s", |
| me, err, m1, (m2 ? m2 : "")); |
| } |
| |
| static void term(int sig LTP_ATTRIBUTE_UNUSED) |
| { |
| int i; |
| |
| tst_resm(TINFO, "\tterm -[%d]- got sig term.", getpid()); |
| |
| if (parent_pid == getpid()) { |
| for (i = 0; i < nchild; i++) |
| if (pidlist[i]) |
| kill(pidlist[i], SIGTERM); |
| return; |
| } |
| |
| tst_brkm(TBROK, NULL, "Term: Child process exiting."); |
| } |
| |
| static void cleanup(void) |
| { |
| char mount_buffer[1024]; |
| |
| if (mnt == 1) { |
| if (chdir(startdir) < 0) { |
| tst_resm(TINFO, "Could not change to %s ", startdir); |
| } |
| if (!strcmp(fstyp, "cfs")) { |
| sprintf(mount_buffer, "/bin/umount %s", partition); |
| if (system(mount_buffer) != 0) { |
| tst_resm(TINFO, "Unable to unmount %s from %s ", |
| partition, mntpoint); |
| if (umount(partition)) { |
| tst_resm(TINFO, |
| "Unable to unmount %s from %s ", |
| partition, mntpoint); |
| } else { |
| tst_resm(TINFO, |
| "Forced umount for %s, /etc/mtab now dirty", |
| partition); |
| } |
| } |
| } else { |
| if (umount(partition)) { |
| tst_resm(TINFO, "Unable to unmount %s from %s ", |
| partition, mntpoint); |
| } |
| } |
| if (rmdir(mntpoint) != 0) { |
| tst_resm(TINFO, "Unable to rmdir %s ", mntpoint); |
| } |
| } |
| tst_rmdir(); |
| |
| } |