#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>


int
main (void)
{
  int result = 0;
  FILE *fp;
  size_t c;
  char buf[1000];
  int fd;
  unsigned char *ptr;
  size_t ps = sysconf (_SC_PAGESIZE);
  void *mem;

  /* Create a file and put some data in it.  */
  fp = tmpfile ();
  if (fp == NULL)
    {
      printf ("Cannot create temporary file: %m\n");
      return 1;
    }
  fd = fileno (fp);

  for (c = 0; c < sizeof (buf); ++c)
    buf[c] = '0' + (c % 10);

  for (c = 0; c < (ps * 4) / sizeof (buf); ++c)
    if (fwrite (buf, 1, sizeof (buf), fp) != sizeof (buf))
      {
	printf ("`fwrite' failed: %m\n");
	return 1;
      }
  fflush (fp);
  assert (ps + 1000 < c * sizeof (buf));

  /* First try something which is not allowed: map at an offset which is
     not modulo the pagesize.  */
  ptr = mmap (NULL, 1000, PROT_READ, MAP_SHARED, fd, ps - 1);
  if (ptr != MAP_FAILED)
    {
      puts ("mapping at offset with mod pagesize != 0 succeeded!");
      result = 1;
    }
  else if (errno != EINVAL && errno != ENOSYS)
    {
      puts ("wrong error value for mapping at offset with mod pagesize != 0: %m (should be EINVAL)");
      result = 1;
    }

  /* Try the same for mmap64.  */
  ptr = mmap64 (NULL, 1000, PROT_READ, MAP_SHARED, fd, ps - 1);
  if (ptr != MAP_FAILED)
    {
      puts ("mapping at offset with mod pagesize != 0 succeeded!");
      result = 1;
    }
  else if (errno != EINVAL && errno != ENOSYS)
    {
      puts ("wrong error value for mapping at offset with mod pagesize != 0: %m (should be EINVAL)");
      result = 1;
    }

  /* And the same for private mapping.  */
  ptr = mmap (NULL, 1000, PROT_READ, MAP_PRIVATE, fd, ps - 1);
  if (ptr != MAP_FAILED)
    {
      puts ("mapping at offset with mod pagesize != 0 succeeded!");
      result = 1;
    }
  else if (errno != EINVAL && errno != ENOSYS)
    {
      puts ("wrong error value for mapping at offset with mod pagesize != 0: %m (should be EINVAL)");
      result = 1;
    }

  /* Try the same for mmap64.  */
  ptr = mmap64 (NULL, 1000, PROT_READ, MAP_PRIVATE, fd, ps - 1);
  if (ptr != MAP_FAILED)
    {
      puts ("mapping at offset with mod pagesize != 0 succeeded!");
      result = 1;
    }
  else if (errno != EINVAL && errno != ENOSYS)
    {
      puts ("wrong error value for mapping at offset with mod pagesize != 0: %m (should be EINVAL)");
      result = 1;
    }

  /* Get a valid address.  */
  mem = malloc (2 * ps);
  if (mem != NULL)
    {
      /* Now we map at an address which is not mod pagesize.  */
      ptr = mmap (mem + 1, 1000, PROT_READ, MAP_SHARED | MAP_FIXED, fd, ps);
      if (ptr != MAP_FAILED)
	{
	  puts ("mapping at address with mod pagesize != 0 succeeded!");
	  result = 1;
	}
      else  if (errno != EINVAL && errno != ENOSYS)
	{
	  puts ("wrong error value for mapping at address with mod pagesize != 0: %m (should be EINVAL)");
	  result = 1;
	}

      /* Try the same for mmap64.  */
      ptr = mmap64 (mem + 1, 1000, PROT_READ, MAP_SHARED | MAP_FIXED, fd, ps);
      if (ptr != MAP_FAILED)
	{
	  puts ("mapping at address with mod pagesize != 0 succeeded!");
	  result = 1;
	}
      else  if (errno != EINVAL && errno != ENOSYS)
	{
	  puts ("wrong error value for mapping at address with mod pagesize != 0: %m (should be EINVAL)");
	  result = 1;
	}

      /* And again for MAP_PRIVATE.  */
      ptr = mmap (mem + 1, 1000, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ps);
      if (ptr != MAP_FAILED)
	{
	  puts ("mapping at address with mod pagesize != 0 succeeded!");
	  result = 1;
	}
      else  if (errno != EINVAL && errno != ENOSYS)
	{
	  puts ("wrong error value for mapping at address with mod pagesize != 0: %m (should be EINVAL)");
	  result = 1;
	}

      /* Try the same for mmap64.  */
      ptr = mmap64 (mem + 1, 1000, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ps);
      if (ptr != MAP_FAILED)
	{
	  puts ("mapping at address with mod pagesize != 0 succeeded!");
	  result = 1;
	}
      else  if (errno != EINVAL && errno != ENOSYS)
	{
	  puts ("wrong error value for mapping at address with mod pagesize != 0: %m (should be EINVAL)");
	  result = 1;
	}

      free (mem);
    }

  /* Now map the memory and see whether the content of the mapped area
     is correct.  */
  ptr = mmap (NULL, 1000, PROT_READ, MAP_SHARED, fd, ps);
  if (ptr == MAP_FAILED)
    {
      if (errno != ENOSYS)
	{
	  printf ("cannot mmap file: %m\n");
	  result = 1;
	}
    }
  else
    {
      for (c = ps; c < ps + 1000; ++c)
	if (ptr[c - ps] != '0' + (c % 10))
	  {
	    printf ("wrong data mapped at offset %zd\n", c);
	    result = 1;
	  }
    }

  /* And for mmap64. */
  ptr = mmap64 (NULL, 1000, PROT_READ, MAP_SHARED, fd, ps);
  if (ptr == MAP_FAILED)
    {
      if (errno != ENOSYS)
	{
	  printf ("cannot mmap file: %m\n");
	  result = 1;
	}
    }
  else
    {
      for (c = ps; c < ps + 1000; ++c)
	if (ptr[c - ps] != '0' + (c % 10))
	  {
	    printf ("wrong data mapped at offset %zd\n", c);
	    result = 1;
	  }
    }

  /* That's it.  */
  return result;
}
