#!/usr/bin/env python3 import os import sys import tempfile from posix_parity import fail from posix_parity import join from posix_parity import mergerfs_get_option from posix_parity import mergerfs_set_option from posix_parity import touch def list_and_stat(dirpath): out = {} for name in os.listdir(dirpath): if name in (".", ".."): continue st = os.lstat(os.path.join(dirpath, name)) out[name] = (st.st_mode & 0xF000, st.st_size) return out def main(): if len(sys.argv) != 2: print("usage: TEST_posix_readdir_plus ", file=sys.stderr) return 1 mount = sys.argv[1] with tempfile.TemporaryDirectory() as native: merge_dir = join(mount, "posix-readdir-plus/dir") native_dir = join(native, "posix-readdir-plus/dir") for i in range(40): touch(join(merge_dir, f"f{i:03d}"), bytes(str(i), "ascii")) touch(join(native_dir, f"f{i:03d}"), bytes(str(i), "ascii")) # Exercise mapping from readdir entries to immediate getattr calls. m_map = list_and_stat(merge_dir) n_map = list_and_stat(native_dir) if m_map != n_map: return fail("readdir+getattr map mismatch between mergerfs and native") # Optionally toggle readdir policy and ensure semantic output stays stable. try: orig = mergerfs_get_option(mount, "func.readdir") except (PermissionError, FileNotFoundError, OSError): return 0 try: for mode in ("seq", "cosr:2:2", "cor:2:2"): mergerfs_set_option(mount, "func.readdir", mode) got = list_and_stat(merge_dir) if got != m_map: return fail(f"func.readdir={mode} changed readdir+getattr semantic output") except (PermissionError, FileNotFoundError, OSError): return 0 finally: try: mergerfs_set_option(mount, "func.readdir", orig) except OSError: pass return 0 if __name__ == "__main__": raise SystemExit(main())