%global lustre_version 2.17.0
%global kver 6.12.0-211.18.1.el10_2.x86_64
%global ksrc /usr/src/kernels/%{kver}

%global debug_package %{nil}

# Avoid RPM hardening/LTO/PIE flags leaking into external kernel module builds.
%global _hardened_build 0
%global _annotated_build 0
%global _lto_cflags %{nil}
%global optflags -O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic
%global build_cflags -O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic
%global __global_cflags -O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic
%global build_ldflags -no-pie
%global __global_ldflags -no-pie

# We generate file lists during %%install. If brp-compress later compresses
# man pages, the generated file list becomes stale. Disable it.
%global __brp_compress %{nil}

Name:           lustre
Version:        %{lustre_version}
Release:        5%{?dist}
Summary:        Lustre filesystem client packages for RHEL 10.2

License:        GPL-2.0-or-later
URL:            https://www.lustre.org/
Source0:        lustre-%{lustre_version}.tar.gz

ExclusiveArch:  x86_64

BuildRequires:  gcc
BuildRequires:  make
BuildRequires:  autoconf
BuildRequires:  automake
BuildRequires:  libtool
BuildRequires:  python3
BuildRequires:  kernel-devel-uname-r = %{kver}
BuildRequires:  elfutils-libelf-devel
BuildRequires:  openssl-devel
BuildRequires:  zlib-devel
BuildRequires:  libuuid-devel
BuildRequires:  libmount-devel
BuildRequires:  libnl3-devel
BuildRequires:  readline-devel
BuildRequires:  ncurses-devel
BuildRequires:  e2fsprogs-devel
BuildRequires:  keyutils-libs-devel
BuildRequires:  bison
BuildRequires:  flex
BuildRequires:  bc

Requires:       lustre-client = %{version}-%{release}

%description
Lustre %{lustre_version} client metapackage for RHEL 10.2.

This custom spec builds Lustre client-side userspace tools and client kernel
modules for kernel %{kver}. It applies a compatibility patch for RHEL 10.2
kernels where from_timer() has been replaced by timer_container_of().


%package client
Summary:        Lustre client userspace tools
Requires:       kmod-lustre-client = %{version}-%{release}

%description client
Lustre client userspace commands, libraries, configuration files and manual
pages for RHEL 10.2.


%package -n kmod-lustre-client
Summary:        Lustre client kernel modules for kernel %{kver}
Requires:       kernel-core-uname-r = %{kver}
Requires(post): /usr/sbin/depmod
Requires(postun): /usr/sbin/depmod
Provides:       lustre-client-kmod = %{version}-%{release}
Provides:       kmod-lustre-client-uname-r = %{kver}

%description -n kmod-lustre-client
Lustre client kernel modules built for RHEL kernel %{kver}.


%prep
%autosetup -n lustre-%{lustre_version}

# ---------------------------------------------------------------------------
# RHEL 10.2 timer compatibility patch:
#
# Lustre 2.17.0 uses:
#
#   from_timer()
#
# but RHEL 10.2 6.12.0-211 may expose:
#
#   timer_container_of()
#
# instead.
# ---------------------------------------------------------------------------
python3 <<'PY'
from pathlib import Path
import re
import sys

p = Path("include/lustre_compat/linux/timer.h")

if not p.exists():
    print(f"ERROR: {p} not found", file=sys.stderr)
    sys.exit(1)

s = p.read_text()

if "timer_container_of(var, callback_timer, timer_fieldname)" in s:
    print("timer.h already contains timer_container_of compatibility logic")
    sys.exit(0)

pattern = re.compile(
    r"#\s*define\s+cfs_from_timer\s*\(\s*var\s*,\s*callback_timer\s*,\s*timer_fieldname\s*\)\s*\\\n"
    r"\s*from_timer\s*\(\s*var\s*,\s*callback_timer\s*,\s*timer_fieldname\s*\)",
    re.MULTILINE,
)

replacement = """#ifndef cfs_from_timer
# ifdef timer_container_of
#  define cfs_from_timer(var, callback_timer, timer_fieldname) \\
        timer_container_of(var, callback_timer, timer_fieldname)
# else
#  define cfs_from_timer(var, callback_timer, timer_fieldname) \\
        from_timer(var, callback_timer, timer_fieldname)
# endif
#endif"""

new, n = pattern.subn(replacement, s, count=1)

if n != 1:
    print("ERROR: could not find expected cfs_from_timer/from_timer definition", file=sys.stderr)
    print("Relevant timer.h lines:", file=sys.stderr)
    for i, line in enumerate(s.splitlines(), 1):
        if "cfs_from_timer" in line or "from_timer" in line or "timer_container_of" in line:
            print(f"{i}: {line}", file=sys.stderr)
    sys.exit(1)

p.write_text(new)
print("Applied RHEL 10.2 timer_container_of compatibility patch")
PY


%build
if [ ! -d "%{ksrc}" ]; then
    echo "ERROR: kernel source tree not found: %{ksrc}"
    echo "Install matching kernel-devel package first:"
    echo "  dnf install kernel-devel-%{kver}"
    exit 1
fi

# Clean RPM build environment for kernel module build.
unset CFLAGS
unset CXXFLAGS
unset CPPFLAGS
unset LDFLAGS
unset FFLAGS
unset FCFLAGS
unset RPM_OPT_FLAGS
unset EXTRA_CFLAGS
unset EXTRA_KCFLAGS
unset KBUILD_CFLAGS_MODULE

export CFLAGS="-O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic"
export CXXFLAGS="-O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic"
export LDFLAGS="-no-pie"
export KCFLAGS="-fno-PIE -fno-pie -fno-PIC -fno-pic"

if [ ! -x ./configure ]; then
    if [ -x ./autogen.sh ]; then
        ./autogen.sh
    else
        echo "ERROR: ./configure not found and ./autogen.sh not executable"
        exit 1
    fi
fi

./configure \
    --with-linux=%{ksrc} \
    --with-linux-obj=%{ksrc} \
    --with-o2ib=no \
    --disable-server

# Do not use %%make_build here.
# Do not pass CPPFLAGS/CFLAGS/LDFLAGS on the make command line.
make V=1 %{?_smp_mflags}


%install
rm -rf %{buildroot}

unset CFLAGS
unset CXXFLAGS
unset CPPFLAGS
unset LDFLAGS
unset FFLAGS
unset FCFLAGS
unset RPM_OPT_FLAGS
unset EXTRA_CFLAGS
unset EXTRA_KCFLAGS
unset KBUILD_CFLAGS_MODULE

export CFLAGS="-O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic"
export CXXFLAGS="-O2 -g -fno-PIE -fno-pie -fno-PIC -fno-pic"
export LDFLAGS="-no-pie"
export KCFLAGS="-fno-PIE -fno-pie -fno-PIC -fno-pic"

make install DESTDIR=%{buildroot}

# Remove libtool/static leftovers.
find %{buildroot} -name '*.la' -delete || :
find %{buildroot} -name '*.a' -delete || :

# Ensure modules are under /lib/modules/<kver>/extra/lustre.
if ! find %{buildroot}/lib/modules/%{kver} -name '*.ko' -print 2>/dev/null | grep -q . ; then
    mkdir -p %{buildroot}/lib/modules/%{kver}/extra/lustre
    find . -name '*.ko' -exec cp -a '{}' %{buildroot}/lib/modules/%{kver}/extra/lustre/ \;
fi

if ! find %{buildroot}/lib/modules/%{kver} -name '*.ko' -print 2>/dev/null | grep -q . ; then
    echo "ERROR: no Lustre kernel modules found in buildroot"
    exit 1
fi

# Do not package generated module dependency files.
find %{buildroot}/lib/modules/%{kver} \
    \( -name modules.dep -o \
       -name modules.dep.bin -o \
       -name modules.alias -o \
       -name modules.alias.bin -o \
       -name modules.symbols -o \
       -name modules.symbols.bin -o \
       -name modules.builtin.bin -o \
       -name modules.devname -o \
       -name modules.softdep \) \
    -delete 2>/dev/null || :

# ---------------------------------------------------------------------------
# Create a small file owned by the base "lustre" metapackage.
# This prevents the base package from being completely empty.
# ---------------------------------------------------------------------------
mkdir -p %{buildroot}%{_docdir}/lustre
cat > %{buildroot}%{_docdir}/lustre/README.rhel10-custom <<EOF
Custom Lustre %{lustre_version} client build for RHEL 10.2 kernel %{kver}.

This build applies a compatibility patch for timer_container_of() on RHEL 10.2.
EOF

# ---------------------------------------------------------------------------
# Generate subpackage file lists.
#
# kmod-lustre-client:
#   only kernel modules under /lib/modules/<kver>
#
# lustre-client:
#   all installed userspace files, configs, libs, commands, man pages, etc.,
#   excluding kernel modules and excluding the README owned by base lustre.
#
# lustre:
#   owns only the README and requires lustre-client.
# ---------------------------------------------------------------------------

: > %{_builddir}/lustre.files
: > %{_builddir}/lustre-client.files
: > %{_builddir}/kmod-lustre-client.files

echo "%doc %{_docdir}/lustre/README.rhel10-custom" > %{_builddir}/lustre.files

find %{buildroot}/lib/modules/%{kver} \( -type f -o -type l \) -name '*.ko' \
    | sort \
    | sed "s#^%{buildroot}##" \
    > %{_builddir}/kmod-lustre-client.files

find %{buildroot} \( -type f -o -type l \) \
    | sort \
    | sed "s#^%{buildroot}##" \
    | grep -v "^/lib/modules/%{kver}/.*\.ko$" \
    | grep -v "^%{_docdir}/lustre/README.rhel10-custom$" \
    > %{_builddir}/lustre-client.files.raw

awk '
{
    if ($0 ~ "^/etc/") {
        print "%config(noreplace) " $0
    } else {
        print $0
    }
}
' %{_builddir}/lustre-client.files.raw > %{_builddir}/lustre-client.files

# Final sanity checks.
if [ ! -s %{_builddir}/kmod-lustre-client.files ]; then
    echo "ERROR: kmod-lustre-client file list is empty"
    exit 1
fi

if [ ! -s %{_builddir}/lustre-client.files ]; then
    echo "ERROR: lustre-client file list is empty"
    exit 1
fi


%post -n kmod-lustre-client
/usr/sbin/depmod -a %{kver} || :


%postun -n kmod-lustre-client
/usr/sbin/depmod -a %{kver} || :


%files -f %{_builddir}/lustre.files
%defattr(-,root,root,-)


%files client -f %{_builddir}/lustre-client.files
%defattr(-,root,root,-)


%files -n kmod-lustre-client -f %{_builddir}/kmod-lustre-client.files
%defattr(-,root,root,-)


%changelog
* Mon Jun 01 2026 Georgios Magklaras <georgiosm@met.no> - 2.17.0-5
- Produce split RPMs: lustre, lustre-client, kmod-lustre-client
- Keep RHEL 10.2 timer_container_of compatibility patch
- Keep RPM hardening/LTO/PIE flags out of external kernel module build
- Avoid stale generated file list from man-page compression

* Mon Jun 01 2026 Georgios Magklaras <georgiosm@met.no> - 2.17.0-4
- Disable brp-compress because dynamic file list is generated before man-page compression
- Avoid final RPM packaging failure on missing uncompressed man-page paths

* Mon Jun 01 2026 Georgios Magklaras <georgiosm@met.no> - 2.17.0-3
- Do not pass CPPFLAGS/CFLAGS/LDFLAGS on make command line
- Fix lost libcfs include path causing libcfs/util/string.h not found
- Keep RPM hardening/LTO/PIE flags out of kernel module build

* Mon Jun 01 2026 Georgios Magklaras <georgiosm@met.no> - 2.17.0-2
- Prevent RPM hardening, annobin, LTO and PIE flags from leaking into kernel module build
- Add explicit non-PIE/non-PIC flags for RHEL 10.2 external module build

* Mon Jun 01 2026 Georgios Magklaras <georgiosm@met.no> - 2.17.0-1
- Build Lustre 2.17.0 client for RHEL 10.2 kernel 6.12.0-211.18.1.el10_2.x86_64
- Add compatibility handling for timer_container_of() replacing from_timer()

