#define _GNU_SOURCE
#include "file_fixture.h"
#include <sys/mman.h>

int main(void)
{
    struct file_fixture file = {.fd = -1};
    int result = EXIT_FAILURE;
    unsigned char *shared = MAP_FAILED, *private = MAP_FAILED;
    if (file_fixture_open(&file, "./mmap-cow-XXXXXX") == -1)
        goto out;
    if (file_byte_write(file.fd, 'A') == -1)
        goto out;
    shared = mmap(NULL, file.length, PROT_READ | PROT_WRITE,
                  MAP_SHARED, file.fd, 0);
    if (shared == MAP_FAILED)
        goto out;
    private = mmap(NULL, file.length, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE, file.fd, 0);
    if (private == MAP_FAILED)
        goto out;
    private[0] = 'P';
    shared[0] = 'S';
    if (msync(shared, file.length, MS_SYNC) == -1)
        goto out;
    unsigned char file_byte = 0;
    ssize_t count = pread(file.fd, &file_byte, 1, 0);
    if (count != 1) {
        if (count >= 0)
            errno = EIO;
        goto out;
    }
    printf("shared write: file=%c private=%c\n", file_byte, private[0]);
    if (file_byte != 'S' || private[0] != 'P') {
        errno = EPROTO;
        goto out;
    }
    /* An in-place overwrite: no O_TRUNC and no size change. */
    if (file_byte_write(file.fd, 'W') == -1)
        goto out;
    printf("pwrite: shared=%c private=%c\n", shared[0], private[0]);
    result = EXIT_SUCCESS;
out:
    if (result != EXIT_SUCCESS)
        perror("file/CoW lab");
    if (private != MAP_FAILED && munmap(private, file.length) == -1) {
        perror("private munmap");
        result = EXIT_FAILURE;
    }
    if (shared != MAP_FAILED && munmap(shared, file.length) == -1) {
        perror("shared munmap");
        result = EXIT_FAILURE;
    }
    if (file_fixture_close(&file) == -1)
        result = EXIT_FAILURE;
    return result;
}
