/*
 * mmap-bug.c: test for the presense of mmap bug with append-only
 * files.  if it fails (and the bug is not present), it will probably
 * exit with an error from a system call.  this program will only
 * compile on systems with 4.4BSD-compatible `file flags'.
 *
 * Copyright (c) 1998 Matthew Green.  All Rights Reserved.
 */

#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

char filedata[] = "you do NOT have the bug.\n";
char data[] = "you do have the bug.\n";

void child __P((const char *));

int
main(argc, argv)
	int argc;
	char *argv[];
{
	caddr_t f;
	pid_t pid;
	int fd;

	if (argc < 2)
		errx(1, "usage: mmap-bug <file>");

	/* first create the file, and set APPEND */
	fd = open(argv[1], O_CREAT|O_TRUNC|O_WRONLY, 0644);
	if (fd < 0)
		err(1, "open");
	if (write(fd, filedata, sizeof filedata) < 0)
		err(1, "write");
	if (fchflags(fd, SF_APPEND|UF_APPEND) < 0)
		err(1, "fchflags");
	if (close(fd) < 0)
		err(1, "close");

	/* now fork the child */
	pid = fork();
	if (pid < 0)
		err(1, "fork");
	if (pid == 0)
		child(argv[1]);

	/* ok, in parent: open file append/read/write, and map it in */
	fd = open(argv[1], O_APPEND|O_RDWR, 0);
	if (fd < 0)
		err(1, "parent open");
	f = mmap(0, 4096, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
	if (f == (caddr_t)-1)
		err(1, "parent mmap");

	/* modify the file, and write it out */
	strcpy(f, data);

	/* wait for the child, and clean up */
	wait(NULL);
	if (fchflags(fd, 0) < 0)
		err(1, "fchflags 2");
	if (unlink(argv[1]) < 0)
		err(1, "unlink");

	exit(0);
}

void
child(path)
	const char *path;
{
	caddr_t f;
	int fd;

	sleep(3);

	/* ok, in child: open file read, and map it in */
	fd = open(path, O_RDONLY);
	if (fd < 0)
		err(1, "child open");
	f = mmap(0, 4096, PROT_READ, MAP_SHARED, fd, 0);
	if (f == (caddr_t)-1)
		err(1, "child mmap");

	/* write it out */
	write(1, f, strlen(f));

	exit(0);
}
