#!/bin/sh
# Generate layout-check macros for the hand-copied struct getdents_callback64
# in fs/dirent.c, from the target kernel's vmlinux via pahole (BTF/DWARF).
#
# Usage: getdents-layout.sh <vmlinux-or-empty> <output.h>
#
# Three modes:
#   - no vmlinux argument (in-tree build: vmlinux doesn't exist yet when
#     we're compiled, but we're built from the same tree as fs/readdir.c):
#     TRUSTED - fastpath enabled, no asserts.
#   - vmlinux readable + pahole finds the struct (external/dkms build):
#     VERIFIED - emit member offsets, fs/dirent.c static_asserts the copy.
#   - anything else: UNVERIFIED - fs/dirent.c disables the filldir64
#     fastpath; readdir falls back to the unlock path. A perf regression,
#     never a layout gamble.

VMLINUX="$1"
OUT="$2"
TMP="$OUT.tmp"

{
	echo "/* Generated by fs/scripts/getdents-layout.sh - do not edit */"

	if [ -z "$VMLINUX" ]; then
		echo "#define BCH_GETDENTS_LAYOUT_TRUSTED 1 /* in-tree build */"
	elif command -v pahole >/dev/null 2>&1 && [ -r "$VMLINUX" ] &&
	     P=$(pahole -C getdents_callback64 "$VMLINUX" 2>/dev/null) &&
	     [ -n "$P" ]; then
		echo "#define BCH_GETDENTS_LAYOUT_VERIFIED 1"
		printf '%s\n' "$P" | awk '
			# member lines: "<type> <name>; /* <off> <size> */"
			/;[ \t]*\/\*/ {
				name = ""
				for (i = 1; i <= NF; i++)
					if ($i ~ /;$/) {
						name = $i
						sub(/;.*/, "", name)
					}
				# strip any leading "*" from pointer declarators
				sub(/^\*+/, "", name)
				if (name == "")
					next
				match($0, /\/\*[ \t]*[0-9]+/)
				off = substr($0, RSTART + 2)
				sub(/^[ \t]*/, "", off)
				sub(/[ \t].*/, "", off)
				printf "#define BCH_GETDENTS_OFF_%s %s\n", name, off
			}
			/\/\* size: [0-9]+/ {
				match($0, /size: [0-9]+/)
				s = substr($0, RSTART + 6)
				sub(/,.*/, "", s)
				printf "#define BCH_GETDENTS_SIZE %s\n", s
			}
		'
	else
		echo "#define BCH_GETDENTS_LAYOUT_UNVERIFIED 1"
	fi
} > "$TMP"

if cmp -s "$TMP" "$OUT" 2>/dev/null; then
	rm -f "$TMP"
else
	mv "$TMP" "$OUT"
fi
