/*
 * Check decoding and dumping of read and write syscalls.
 *
 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
 * Copyright (c) 2016-2021 The strace developers.
 * All rights reserved.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>

# define LENGTH_OF(arg) ((unsigned int) sizeof(arg) - 1)
# define ARRAY_SIZE(a_)        (sizeof(a_) / sizeof((a_)[0]))

static void *
tail_alloc(const size_t size)
{
	const size_t page_size = sysconf(_SC_PAGESIZE);
	const size_t len = (size + page_size - 1) & -page_size;
	const size_t alloc_size = len + 6 * page_size;

	void *p = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE,
		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if (MAP_FAILED == p) {
		perror("mmap");
		exit(1);
	}

	void *start_work = p + 3 * page_size;
	void *tail_guard = start_work + len;

	if (munmap(p, page_size) ||
	    munmap(p + 2 * page_size, page_size) ||
	    munmap(tail_guard, page_size) ||
	    munmap(tail_guard + 2 * page_size, page_size)) {
		perror("munmap");
		exit(1);
	}

	memset(start_work, 0xff, len);
	return tail_guard - size;
}

static void
fill_memory_ex(void *ptr, size_t size, unsigned char start,
	       unsigned int period)
{
	unsigned char *p = ptr;

	for (typeof(size) i = 0; i < size; ++i) {
		p[i] = start + i % period;
	}
}

int
main(void)
{
	static const char tmp[] = "read-write-tmpfile";
	long rc;
	long fdr;
	long fdw;

	unlink(tmp);

	fdr = open(tmp, O_CREAT|O_EXCL|O_RDONLY, 0600);
	if (fdr < 0) {
		perror("create");
		exit(1);
	}

	fdw = open(tmp, O_TRUNC|O_WRONLY);
	if (fdw < 0) {
		perror("open");
		exit(1);
	}

	static const char w_c[] = "0123456789abcde";
	const unsigned int w_len = LENGTH_OF(w_c);

	rc = write(fdw, w_c, w_len);
	if (rc != (int) w_len) {
		perror("write");
		exit(1);
	}

	static const size_t six_wide_size = 1 << 20;
	static const size_t fetch_size = 1 << 16;
	const size_t buf_size = six_wide_size + fetch_size;
	const size_t sizes[] = {
		buf_size,
		buf_size + 1,
	};
	char *big_buf = tail_alloc(buf_size);

	fill_memory_ex(big_buf, buf_size, 0, 0x100);

	for (size_t i = 0; i < ARRAY_SIZE(sizes); i++) {
		write(fdw, big_buf, sizes[i]);
	}

	close(fdw);

	return 0;
}
