/*
*
* 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 "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;
/*
* parse standard options
*/
tst_parse_opts(ac, av, NULL, NULL);
/*
* 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 ARRAY_SIZE(ino_thing)
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();
}