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

int main(void)
{
    long page = sysconf(_SC_PAGESIZE);
    if (page <= 0) {
        fputs("cannot determine page size\n", stderr);
        return EXIT_FAILURE;
    }
    size_t unit = (size_t)page, length = 3 * unit;
    unsigned char *arena = mmap(NULL, length, PROT_NONE,
                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (arena == MAP_FAILED) {
        perror("reserve arena");
        return EXIT_FAILURE;
    }
    int result = EXIT_FAILURE;
    unsigned char *slot = arena + unit;
    errno = 0;
    void *probe = mmap(slot, unit, PROT_READ | PROT_WRITE,
                       MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
                       -1, 0);
    if (probe != MAP_FAILED) {
        /* Older kernels can ignore NOREPLACE and return another address. */
        if (probe != slot && munmap(probe, unit) == -1)
            perror("unmap unexpected result");
        fputs("expected occupied-address rejection\n", stderr);
        goto out;
    }
    if (errno != EEXIST) {
        perror("MAP_FIXED_NOREPLACE");
        goto out;
    }
    puts("NOREPLACE rejected the occupied slot");
    /* Only replace a page inside our still-reserved, private arena. */
    void *mapped = mmap(slot, unit, PROT_READ | PROT_WRITE,
                        MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
    if (mapped == MAP_FAILED) {
        perror("replace owned slot");
        goto out;
    }
    if (mapped != slot) {
        fputs("unexpected fixed mapping address\n", stderr);
        if (munmap(mapped, unit) == -1)
            perror("unmap unexpected fixed result");
        goto out;
    }
    slot[0] = 53;
    printf("owned middle page: %u\n", (unsigned)slot[0]);
    result = EXIT_SUCCESS;
out:
    if (munmap(arena, length) == -1) {
        perror("release arena");
        result = EXIT_FAILURE;
    }
    return result;
}
