#define _GNU_SOURCE   1      /* See feature_test_macros(7) */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>

#define MBSIZE(x) ((long) (x) * 1024 * 1024)

static void
resize_map_and_test(void *memaddr, int fd, size_t size, char *localmem)
{
	int ret;

	if (ftruncate(fd, size) < 0)
	{
		printf("ftruncate failed with errno %d on fd = %d and size = %ld\n", errno, fd, size);
		exit(__LINE__);
	}

	ret = posix_fallocate(fd, 0, size);
	if (ret != 0)
	{
		printf("fallocate failed with errno %d\n", ret);
		exit(__LINE__);
	}

    memset(memaddr, 1, size);
	if (memcmp(memaddr, localmem, size) != 0)
	{
		printf("mmap memory and local memory are not equal upto size = %ld\n", size);
		exit(__LINE__);
	}

	printf("Check memory maps and enter (size %ld):", size);
	getchar();
    /* Causes segfault: memset(memaddr, 0, size + 1); */ 
}

int
main(int argc, char **argv)
{
	int flags = MAP_NORESERVE;
	void *memaddr;
	pid_t pid = getpid();
	char *localmem;
	int fd = memfd_create("mmap_fd_exp", MFD_HUGETLB);
	size_t maxsize = MBSIZE(400);
	size_t size = MBSIZE(300);

	printf("pid = %d\n", pid);

	localmem = malloc(maxsize);
	memset(localmem, 1, maxsize);

	printf("Check initial memory maps and enter:");
	getchar();

	if (fd < 0)
	{
		printf("memfd_create failed with errno %d\n", errno);
		exit(__LINE__);
	}

	memaddr = mmap(NULL, maxsize, PROT_WRITE | PROT_READ /* PROT_NONE */, MAP_SHARED | MAP_NORESERVE | MAP_HUGETLB, fd, 0);
	if (memaddr == MAP_FAILED)
	{
		printf("mmap failed with error %m\n");
		exit(__LINE__);
	}
	printf("Initial mapped file size: %ld\n", lseek(fd, 0, SEEK_END));

	printf("Check memory maps and enter:");
	getchar();

	resize_map_and_test(memaddr, fd, size, localmem);

	resize_map_and_test(memaddr, fd, MBSIZE(100), localmem);
    /* causes a segmentation fault: memset(memaddr, 1, MBSIZE(200)); */ 

	resize_map_and_test(memaddr, fd, MBSIZE(200), localmem);

	resize_map_and_test(memaddr, fd, maxsize, localmem);

	resize_map_and_test(memaddr, fd, MBSIZE(200), localmem);

	close(fd);
}