#!/usr/bin/env python3

import ctypes
import mmap
import os
import resource
import sys
import tempfile


libc = ctypes.CDLL(None, use_errno=True)
libc.read.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t]
libc.read.restype = ctypes.c_ssize_t


def aligned_read(fd, size):
    buf = mmap.mmap(-1, size)
    ptr = ctypes.addressof(ctypes.c_char.from_buffer(buf))
    ctypes.set_errno(0)
    rv = libc.read(fd, ctypes.c_void_p(ptr), size)
    if rv < 0:
        err = ctypes.get_errno()
        buf.close()
        raise OSError(err, os.strerror(err))
    data = buf[:rv]
    buf.close()
    return data


def main():
    (fd, filepath) = tempfile.mkstemp(dir=sys.argv[1])

    os.close(fd)

    fd = os.open(filepath, os.O_RDWR|os.O_DIRECT|os.O_TRUNC)

    os.unlink(filepath)

    size = resource.getpagesize()
    pattern = (b"mergerfs-o-direct" * ((size // 16) + 1))[:size]
    buf = mmap.mmap(-1, size)
    buf.write(pattern)
    buf.seek(0)

    try:
        written = os.write(fd, buf)
        if written != size:
            print(f"O_DIRECT short write: wrote {written}, expected {size}", end="")
            return 1
        os.lseek(fd, 0, os.SEEK_SET)
        data = aligned_read(fd, size)
        if len(data) != size:
            print(f"O_DIRECT short read: read {len(data)}, expected {size}", end="")
            return 1
        if data != pattern:
            print("O_DIRECT data mismatch", end="")
            return 1
    finally:
        buf.close()
        os.close(fd)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
