#include <cstdlib>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <unistd.h>

#include <linux/udmabuf.h>

namespace {

int createMemfd(size_t bytes, unsigned int flags) {

    int fd(memfd_create("memfd_test", flags));
    if (fd == -1) {
        perror("memfd_create");
        return -1;
    }

    int rc = ftruncate(fd, bytes);
    if (rc == -1) {
        perror("ftruncate");
        return -1;
    }

    return fd;
}

int createUdmabuf(size_t bytes, int memfd) {
    int udmabuf_dev_fd(open("/dev/udmabuf", O_RDONLY));
    if (udmabuf_dev_fd == -1) {
        perror("udmabuf open");
        return -1;
    }

    struct udmabuf_create create;
    create.memfd = memfd;
    create.flags = UDMABUF_FLAGS_CLOEXEC;
    create.offset = 0;
    create.size = bytes;

    int dmabuf_fd(ioctl(udmabuf_dev_fd, UDMABUF_CREATE, &create));
    if (dmabuf_fd == -1) perror("UDMABUF_CREATE");

    return dmabuf_fd;
}

} // anonymous namespace


int main(int argc, char **argv) {
	size_t bytes = 1ull << 30;

	int memfd = createMemfd(bytes, MFD_ALLOW_SEALING);
	if (memfd < 0) return EXIT_FAILURE;

	if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK) < 0) {
		perror("F_SEAL_SHRINK");
		return EXIT_FAILURE;
	}

    if (ftruncate(memfd, bytes)) {
        perror("ftruncate");
        return EXIT_FAILURE;
    }

    int bigfile = open(argv[1], O_RDONLY | O_DIRECT);
    if (bigfile < 0) {
        perror("open");
        return EXIT_FAILURE;
    }

    off_t offset = 0;
    for (ssize_t n = sendfile(memfd, bigfile, &offset, bytes - offset);
         offset < bytes && n > 0;
         n = sendfile(memfd, bigfile, &offset, bytes - offset))
    {}
    printf("Offset %ld\n", offset);

    int udmabuf = createUdmabuf(bytes, memfd);
    if (udmabuf < 0) return EXIT_FAILURE;

    return EXIT_SUCCESS;
}
