| /* |
| * |
| * 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 |
| * ftest02.c -- test inode things (ported from SPIE section2, filesuite, by Airong Zhang) |
| * |
| * CALLS |
| * open, close, read, write, lseek, |
| * unlink, chdir |
| * |
| * |
| * ALGORITHM |
| * |
| * |
| * ftest02 [-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!! |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/wait.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <sys/mount.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include "test.h" |
| #include "libftest.h" |
| |
| #define MAXCHILD 25 |
| #define K_1 1024 |
| #define K_2 2048 |
| #define K_4 4096 |
| |
| char *TCID = "ftest02"; |
| 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 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 char tmpname[MAXPATHLEN]; |
| static int dirlen; |
| static int mnt = 0; |
| static char startdir[MAXPATHLEN], mntpoint[MAXPATHLEN]; |
| static char *partition; |
| static char *cwd; |
| static char *fstyp; |
| static int local_flag; |
| |
| int main(void) |
| { |
| int k, j, pid, child, status, count; |
| char name[128]; |
| |
| /* |
| * Default values for run conditions. |
| */ |
| iterations = 50; |
| nchild = 5; |
| |
| if (signal(SIGTERM, term) == SIG_ERR) { |
| tst_resm(TFAIL, "first signal failed"); |
| |
| } |
| |
| /* |
| * Make a directory to do this in; ignore error if already exists. |
| */ |
| local_flag = PASSED; |
| parent_pid = getpid(); |
| tst_tmpdir(); |
| |
| if (!startdir[0]) { |
| if (getcwd(startdir, MAXPATHLEN) == NULL) { |
| tst_resm(TBROK, "getcwd failed"); |
| |
| } |
| } |
| cwd = startdir; |
| strcat(dirname, cwd); |
| sprintf(tmpname, "/ftest02.%d", getpid()); |
| strcat(dirname, tmpname); |
| strcat(homedir, cwd); |
| sprintf(tmpname, "/ftest02h.%d", getpid()); |
| strcat(homedir, tmpname); |
| |
| mkdir(dirname, 0755); |
| mkdir(homedir, 0755); |
| if (chdir(dirname) < 0) { |
| tst_resm(TBROK, "\tCan't chdir(%s), error %d.", dirname, errno); |
| cleanup(); |
| |
| } |
| dirlen = strlen(dirname); |
| if (chdir(homedir) < 0) { |
| tst_resm(TBROK, "\tCan't chdir(%s), error %d.", homedir, errno); |
| cleanup(); |
| |
| } |
| |
| for (k = 0; k < nchild; k++) { |
| if ((child = fork()) == 0) { |
| dotest(k, iterations); |
| exit(0); |
| } |
| 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); |
| //tst_resm(TINFO,"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 == FAILED) |
| tst_resm(TFAIL, "Test failed in fork-wait part."); |
| else |
| tst_resm(TPASS, "Test passed in fork-wait part."); |
| |
| 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); |
| } |
| |
| chdir(startdir); |
| |
| pid = fork(); |
| |
| if (pid < 0) { |
| tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); |
| } |
| |
| if (pid == 0) { |
| execl("/bin/rm", "rm", "-rf", homedir, NULL); |
| exit(1); |
| } else |
| wait(&status); |
| |
| if (status) |
| tst_resm(TINFO, |
| "CAUTION - ftest02, '%s' may not have been removed.", |
| homedir); |
| |
| pid = fork(); |
| |
| if (pid < 0) { |
| tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); |
| } |
| |
| if (pid == 0) { |
| execl("/bin/rm", "rm", "-rf", dirname, NULL); |
| exit(1); |
| } else |
| wait(&status); |
| |
| if (status) { |
| tst_resm(TINFO, |
| "CAUTION - ftest02, '%s' may not have been removed.", |
| dirname); |
| } |
| |
| sync(); |
| |
| cleanup(); |
| |
| tst_exit(); |
| } |
| |
| #define warn(val,m1,m2) if ((val) < 0) dowarn(me,m1,m2) |
| |
| /* |
| * crfile() |
| * Create a file and write something into it. |
| */ |
| |
| char crmsg[] = "Gee, let's write something in the file!\n"; |
| |
| static void crfile(int me, int count) |
| { |
| int fd, val; |
| char fname[MAXPATHLEN], 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); |
| |
| val = lseek(fd, (rand() % M), 0); |
| warn(val, "lseek", 0); |
| |
| val = write(fd, crmsg, sizeof(crmsg) - 1); |
| warn(val, "write", 0); |
| |
| val = lseek(fd, -(sizeof(crmsg) - 1), 1); |
| warn(val, "lseek", 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 i; |
| int val; |
| 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 i, thing; |
| |
| //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(TBROK, NULL, "Test[%d]: error %d on %s %s", |
| me, err, m1, (m2 ? m2 : "")); |
| } |
| |
| /* |
| * SIGTERM signal handler. |
| */ |
| static void term(int sig LTP_ATTRIBUTE_UNUSED) |
| { |
| int i; |
| |
| if (parent_pid == getpid()) { |
| for (i = 0; i < nchild; i++) |
| if (pidlist[i]) |
| kill(pidlist[i], SIGTERM); |
| return; |
| } |
| |
| tst_brkm(TBROK, NULL, "Child process exiting."); |
| } |
| |
| static void cleanup(void) |
| { |
| char mount_buffer[1024]; |
| |
| if (mnt == 1) { |
| |
| if (chdir(startdir) < 0) |
| tst_resm(TBROK, "Could not change to %s ", startdir); |
| |
| if (!strcmp(fstyp, "cfs")) { |
| |
| sprintf(mount_buffer, "/bin/umount %s", partition); |
| |
| if (system(mount_buffer) != 0) { |
| |
| tst_resm(TBROK, "Unable to unmount %s from %s ", |
| partition, mntpoint); |
| |
| if (umount(partition)) |
| tst_resm(TBROK, |
| "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(TBROK, "Unable to unmount %s from %s ", |
| partition, mntpoint); |
| |
| if (rmdir(mntpoint) != 0) |
| tst_resm(TBROK, "Unable to rmdir %s ", mntpoint); |
| |
| } |
| |
| tst_rmdir(); |
| } |