#!/bin/sh

#
# Copyright (c) 2011 Peter Holm <pho@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

# Scenario from kern/159971
# bstg0003.c by Kirk Russell <kirk ba23 org>

# panic: ino 0xc84c9b00(0x3C8209) 65554, 32780 != 65570
# https://people.freebsd.org/~pho/stress/log/suj23.txt

# panic: first_unlinked_inodedep: prev != next. inodedep = 0xcadf9e00
# https://people.freebsd.org/~pho/stress/log/jeff091.txt

[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1

. ../default.cfg

here=`pwd`
cd /tmp
sed '1,/^EOF/d' < $here/$0 > suj23.c
mycc -o suj23 -Wall -Wextra -O2 suj23.c
rm -f suj23.c

mount | grep "on $mntpoint " | grep -q md$mdstart && umount $mntpoint
[ -c /dev/md$mdstart ] && mdconfig -d -u $mdstart

mdconfig -a -t swap -s 1g -u $mdstart
newfs -j md$mdstart > /dev/null
mount /dev/md$mdstart $mntpoint
chmod 777 $mntpoint

su $testuser -c '/tmp/suj23'

while mount | grep -q "on $mntpoint "; do
	umount $mntpoint || sleep 1
done
mdconfig -d -u $mdstart
rm -f /tmp/suj23
exit 0
EOF
/*
 * Copyright 2011 Kirk J. Russell
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

#include <unistd.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/wait.h>

#define RUNTIME 600

static char *bstg_pathstore[] = {
	"/mnt/111/z",
	"/mnt/111/aaaa",
	"/mnt/111/bbbbb",
	"/mnt/111/ccccc",
	"/mnt/111/d",
	"/mnt/111/e",
	"/mnt/111/ffffff.fff.f",
	"/mnt/111/gggggggggggg",
	"/mnt/111/hhhh",
	"/mnt/111/iiiii.ii",
	"/mnt/111/jjjj.jj.jjjjjjjj",
	"/mnt/111/kkkk.kkkkkkkk",
	"/mnt/111/lllll",
	"/mnt/222/z",
	"/mnt/222/aaaa",
	"/mnt/222/bbbbb",
	"/mnt/222/ccccc",
	"/mnt/222/d",
	"/mnt/222/e",
	"/mnt/222/ffffff.fff.f",
	"/mnt/222/gggggggggggg",
	"/mnt/222/hhhh",
	"/mnt/222/iiiii.ii",
	"/mnt/222/jjjj.jj.jjjjjjjj",
	"/mnt/222/kkkk.kkkkkkkk",
	"/mnt/222/lllll",
	"/mnt/333/z",
	"/mnt/333/aaaa",
	"/mnt/333/bbbbb",
	"/mnt/333/ccccc",
	"/mnt/333/d",
	"/mnt/333/e",
	"/mnt/333/ffffff.fff.f",
	"/mnt/333/gggggggggggg",
	"/mnt/333/hhhh",
	"/mnt/333/iiiii.ii",
	"/mnt/333/jjjj.jj.jjjjjjjj",
	"/mnt/333/kkkk.kkkkkkkk",
	"/mnt/333/lllll",
	"/mnt/444/z",
	"/mnt/444/aaaa",
	"/mnt/444/bbbbb",
	"/mnt/444/ccccc",
	"/mnt/444/d",
	"/mnt/444/e",
	"/mnt/444/ffffff.fff.f",
	"/mnt/444/gggggggggggg",
	"/mnt/444/hhhh",
	"/mnt/444/iiiii.ii",
	"/mnt/444/jjjj.jj.jjjjjjjj",
	"/mnt/444/kkkk.kkkkkkkk",
	"/mnt/444/lllll",
	"/mnt/555/z",
	"/mnt/555/aaaa",
	"/mnt/555/bbbbb",
	"/mnt/555/ccccc",
	"/mnt/555/d",
	"/mnt/555/e",
	"/mnt/555/ffffff.fff.f",
	"/mnt/555/gggggggggggg",
	"/mnt/555/hhhh",
	"/mnt/555/iiiii.ii",
	"/mnt/555/jjjj.jj.jjjjjjjj",
	"/mnt/555/kkkk.kkkkkkkk",
	"/mnt/555/lllll",
	"/mnt/666/z",
	"/mnt/666/aaaa",
	"/mnt/666/bbbbb",
	"/mnt/666/ccccc",
	"/mnt/666/d",
	"/mnt/666/e",
	"/mnt/666/ffffff.fff.f",
	"/mnt/666/gggggggggggg",
	"/mnt/666/hhhh",
	"/mnt/666/iiiii.ii",
	"/mnt/666/jjjj.jj.jjjjjjjj",
	"/mnt/666/kkkk.kkkkkkkk",
	"/mnt/666/lllll",
	"/mnt/777/z",
	"/mnt/777/aaaa",
	"/mnt/777/bbbbb",
	"/mnt/777/ccccc",
	"/mnt/777/d",
	"/mnt/777/e",
	"/mnt/777/ffffff.fff.f",
	"/mnt/777/gggggggggggg",
	"/mnt/777/hhhh",
	"/mnt/777/iiiii.ii",
	"/mnt/777/jjjj.jj.jjjjjjjj",
	"/mnt/777/kkkk.kkkkkkkk",
	"/mnt/777/lllll",
	"/mnt/888/z",
	"/mnt/888/aaaa",
	"/mnt/888/bbbbb",
	"/mnt/888/ccccc",
	"/mnt/888/d",
	"/mnt/888/e",
	"/mnt/888/ffffff.fff.f",
	"/mnt/888/gggggggggggg",
	"/mnt/888/hhhh",
	"/mnt/888/iiiii.ii",
	"/mnt/888/jjjj.jj.jjjjjjjj",
	"/mnt/888/kkkk.kkkkkkkk",
	"/mnt/888/lllll",
	"/mnt/999/z",
	"/mnt/999/aaaa",
	"/mnt/999/bbbbb",
	"/mnt/999/ccccc",
	"/mnt/999/d",
	"/mnt/999/e",
	"/mnt/999/ffffff.fff.f",
	"/mnt/999/gggggggggggg",
	"/mnt/999/hhhh",
	"/mnt/999/iiiii.ii",
	"/mnt/999/jjjj.jj.jjjjjjjj",
	"/mnt/999/kkkk.kkkkkkkk",
	"/mnt/999/lllll",
	"/mnt/aaa/z",
	"/mnt/aaa/aaaa",
	"/mnt/aaa/bbbbb",
	"/mnt/aaa/ccccc",
	"/mnt/aaa/d",
	"/mnt/aaa/e",
	"/mnt/aaa/ffffff.fff.f",
	"/mnt/aaa/gggggggggggg",
	"/mnt/aaa/hhhh",
	"/mnt/aaa/iiiii.ii",
	"/mnt/aaa/jjjj.jj.jjjjjjjj",
	"/mnt/aaa/kkkk.kkkkkkkk",
	"/mnt/aaa/lllll",
	"/mnt/bbb/z",
	"/mnt/bbb/aaaa",
	"/mnt/bbb/bbbbb",
	"/mnt/bbb/ccccc",
	"/mnt/bbb/d",
	"/mnt/bbb/e",
	"/mnt/bbb/ffffff.fff.f",
	"/mnt/bbb/gggggggggggg",
	"/mnt/bbb/hhhh",
	"/mnt/bbb/iiiii.ii",
	"/mnt/bbb/jjjj.jj.jjjjjjjj",
	"/mnt/bbb/kkkk.kkkkkkkk",
	"/mnt/bbb/lllll",
	"/mnt/ccc/z",
	"/mnt/ccc/aaaa",
	"/mnt/ccc/bbbbb",
	"/mnt/ccc/ccccc",
	"/mnt/ccc/d",
	"/mnt/ccc/e",
	"/mnt/ccc/ffffff.fff.f",
	"/mnt/ccc/gggggggggggg",
	"/mnt/ccc/hhhh",
	"/mnt/ccc/iiiii.ii",
	"/mnt/ccc/jjjj.jj.jjjjjjjj",
	"/mnt/ccc/kkkk.kkkkkkkk",
	"/mnt/ccc/lllll",
	"/mnt/ddd/z",
	"/mnt/ddd/aaaa",
	"/mnt/ddd/bbbbb",
	"/mnt/ddd/ccccc",
	"/mnt/ddd/d",
	"/mnt/ddd/e",
	"/mnt/ddd/ffffff.fff.f",
	"/mnt/ddd/gggggggggggg",
	"/mnt/ddd/hhhh",
	"/mnt/ddd/iiiii.ii",
	"/mnt/ddd/jjjj.jj.jjjjjjjj",
	"/mnt/ddd/kkkk.kkkkkkkk",
	"/mnt/ddd/lllll",
	"/mnt/eee/z",
	"/mnt/eee/aaaa",
	"/mnt/eee/bbbbb",
	"/mnt/eee/ccccc",
	"/mnt/eee/d",
	"/mnt/eee/e",
	"/mnt/eee/ffffff.fff.f",
	"/mnt/eee/gggggggggggg",
	"/mnt/eee/hhhh",
	"/mnt/eee/iiiii.ii",
	"/mnt/eee/jjjj.jj.jjjjjjjj",
	"/mnt/eee/kkkk.kkkkkkkk",
	"/mnt/eee/lllll",
	"/mnt/fff/z",
	"/mnt/fff/aaaa",
	"/mnt/fff/bbbbb",
	"/mnt/fff/ccccc",
	"/mnt/fff/d",
	"/mnt/fff/e",
	"/mnt/fff/ffffff.fff.f",
	"/mnt/fff/gggggggggggg",
	"/mnt/fff/hhhh",
	"/mnt/fff/iiiii.ii",
	"/mnt/fff/jjjj.jj.jjjjjjjj",
	"/mnt/fff/kkkk.kkkkkkkk",
	"/mnt/fff/lllll"
};

char *
bstg_pathstore_get()
{
	return bstg_pathstore[rand() %
		     ((sizeof(bstg_pathstore) / sizeof(bstg_pathstore[0])))];
}

void
dogcore()
{
	pid_t sleepchild, gcorechild;
	extern char **environ;

	/* create a child for the gcore target */
	if ((sleepchild = fork()) == 0) {
		sleep(30);
		_exit(1);
	} else if (sleepchild > 0) {
		char *token[] = {NULL, NULL, NULL, NULL, NULL};
		char buf[64];
		int status;

		/* use the first process as the target */
		snprintf(buf, sizeof(buf), "%d", sleepchild);
		token[0] = "gcore";
		token[1] = "-c";
		token[2] = bstg_pathstore_get();
		token[3] = buf;
		assert(token[4] == NULL);

		if ((gcorechild = fork()) > 0) {
			waitpid(gcorechild, &status, 0);
		} else if (gcorechild == 0) {
			execve("/usr/bin/gcore", token, environ);
			_exit(1);
		}
		kill(sleepchild, SIGKILL);
		waitpid(sleepchild, &status, 0);
	}
}

void
dowrite()
{
	struct iovec data[] = {
		{"12", 2},
		{NULL, 0},
		{"12345678", 8},
	};
	static int fd = -1;

	if (fd == -1) {
		/* keep existing file open during life of this process */
		fd = open(bstg_pathstore_get(), O_RDWR | O_NONBLOCK | O_NOCTTY);
	}
	data[1].iov_base = bstg_pathstore_get();
	data[1].iov_len = strlen((char *)data[1].iov_base);
	ftruncate(fd, 0);
	pwritev(fd, data, 3, 0);
}

void
dounlink()
{
	unlink(bstg_pathstore_get());
}

void
dolink()
{
	link(bstg_pathstore_get(), bstg_pathstore_get());
}

void
domkdir()
{
	char **pdir;
	static char *bstg_dirs[] = {
		"/mnt/111", "/mnt/222", "/mnt/333", "/mnt/444",
		"/mnt/555", "/mnt/666", "/mnt/777", "/mnt/888",
		"/mnt/999", "/mnt/aaa", "/mnt/bbb", "/mnt/ccc",
		"/mnt/ddd", "/mnt/eee", "/mnt/fff", NULL
	};

	for (pdir = bstg_dirs; *pdir; pdir++) {
		if (mkdir(*pdir, 0777) == -1)
			err(1, "mkdir(%s)", *pdir);
	}
}

void
dosync()
{
	sync();
}

int
main()
{
	time_t start;
	unsigned x;
	int i, status;
	void (*funcs[]) () = {
		dogcore,
		dowrite,
		dounlink,
		dolink,
		dowrite,
		dounlink,
		dolink,
		dowrite,
		dosync,
		dowrite,
		dounlink,
		dolink,
		dowrite,
		dounlink,
		dolink,
		dowrite,
	};

	/* we only can domkdir() once at startup */
	domkdir();

	/* create 128 children that loop forever running 4 operations */
	dosync();
	for (x = 0; x < 128; x++) {
		if (fork() == 0) {
			/* give child a new seed for the pathname selection */
			srand(x);

			start = time(NULL);
			for (i = 0; i < 1000; i++) {
				/* each child will start looping at different
				 * function */
				(*funcs[x++ % 16]) ();
				if (time(NULL) - start > RUNTIME)
					break;
			}
			/* we never expect this code to run */
			_exit(1);
		}
	}

	/* block forever for all our children */
	while (wait(&status) > 0);
	return 0;
}