/* Test program to demonstrate valgrind breaking fcntl locks during
 * mmap.  Feed it a r/w file, such as its own source code. */

/* See bug 280965. */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <err.h>

int main(int argc, char *argv[])
{
	struct flock fl;
	const char *file = /* argv[1]; */
			   "mmap_fcntl_bug.c";
	int fd, status;
        off_t initial;

	if (!file)
		errx(1, "Usage: %s <normal-file>", argv[0]);

	fd = open(file, O_RDWR);
	if (fd < 0)
		err(1, "Opening %s", file);

        // reproduce bug 297991: mmap interferes with fd position
        initial = lseek(fd, 123, SEEK_SET);
        if (123 != initial)
                err(1, "initial off_t differs from 123 (TEST FAILED)");
        if (lseek(fd, 0, SEEK_CUR) != 123)
                err(1, "zero offset from initial differs from 123 (TEST FAILED)");

	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 1;

	/* I'm assuming no one else tries to lock this! */
	if (fcntl(fd, F_SETLK, &fl) != 0)
		err(1, "Locking %s", file);

	/* If under valgrind, mmap re-opens and closes file, screwing us */
	if (mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0) == MAP_FAILED)
		err(1, "mmap of %s", file);
        if (lseek(fd, 0, SEEK_CUR) != 123)
                errx(1, "zero offset from initial after mmap differs from 123 (TEST FAILED)");

	switch (fork()) {
	case 0:
		/* Child.  Lock should fail. */
		if (fcntl(fd, F_SETLK, &fl) == 0)
			exit(1);
		exit(0);
	case -1:
		err(1, "Fork failed");
	}

	if (wait(&status) == -1)
		 err(1, "Child vanished?");

	if (!WIFEXITED(status))
		errx(1, "Child died with signal %i", WTERMSIG(status));

	switch (WEXITSTATUS(status)) {
	case 1:
		errx(1, "Child got lock, we must have dropped it (TEST FAILED)");
	case 0:
		fprintf(stderr, "Child exited with zero (TEST PASSED).\n");
		return 0;
	default:
		errx(1, "Child weird exit status %i", WEXITSTATUS(status));
	}
}