#!/usr/bin/env python3 import ctypes import os import sys import tempfile from posix_parity import cleanup_dir from posix_parity import fail from posix_parity import join from posix_parity import mergerfs_mount from posix_parity import temp_dir from posix_parity import touch libc = ctypes.CDLL(None, use_errno=True) libc.opendir.argtypes = [ctypes.c_char_p] libc.opendir.restype = ctypes.c_void_p libc.dirfd.argtypes = [ctypes.c_void_p] libc.dirfd.restype = ctypes.c_int libc.closedir.argtypes = [ctypes.c_void_p] libc.closedir.restype = ctypes.c_int def opendir(path): ctypes.set_errno(0) dp = libc.opendir(path.encode()) if not dp: err = ctypes.get_errno() raise OSError(err, os.strerror(err), path) return dp def close_and_dirfd_errno(path): dp = opendir(path) fd = libc.dirfd(dp) if fd < 0: libc.closedir(dp) raise OSError(ctypes.get_errno(), "dirfd failed", path) rv = libc.closedir(dp) if rv != 0: raise OSError(ctypes.get_errno(), "closedir failed", path) try: os.fstat(fd) return 0 except OSError as exc: return exc.errno def main(): try: with mergerfs_mount() as (mount, _): with tempfile.TemporaryDirectory() as native: merge_base = temp_dir(mount) try: native_base = join(native, os.path.basename(merge_base)) os.makedirs(native_base, exist_ok=True) merge_dir = join(merge_base, "dir") native_dir = join(native_base, "dir") touch(join(merge_dir, "a"), b"x") touch(join(native_dir, "a"), b"x") m_err = close_and_dirfd_errno(merge_dir) n_err = close_and_dirfd_errno(native_dir) if m_err != n_err: return fail(f"releasedir EBADF parity mismatch mergerfs_errno={m_err} native_errno={n_err}") return 0 finally: cleanup_dir(merge_base) except RuntimeError as exc: print(str(exc), end="") return 77 if __name__ == "__main__": raise SystemExit(main())