From 93c0a189543b5bf37414b4b7bfe32e2858120bad Mon Sep 17 00:00:00 2001 From: Or Shoval Date: Wed, 25 Feb 2026 06:20:41 +0200 Subject: [PATCH 1/2] kpatch-build: fix Fedora 42+ kernel source directory nesting Starting with Fedora 42, the kernel SRPM unpacks with an extra level of nesting: BUILD/kernel-6.14.0-build/kernel-6.14/linux-6.14.0-63.fc42.x86_64/ The existing glob BUILD/kernel-*/linux-* only matches the traditional flat layout and fails on this structure. Extract the source-finding logic into find_rpm_linux_srcdir() in a new kpatch-funcs.sh, using nullglob with a bash array to safely count flat-glob matches: - Exactly one match: move it (traditional RHEL/CentOS/Fedora < 42). - Multiple matches: die with a clear diagnostic. - Zero matches: search deeper with find -maxdepth 3, excluding configs/ directories, again requiring exactly one result. Signed-off-by: Or Shoval Co-authored-by: Cursor --- kpatch-build/kpatch-build | 3 ++- kpatch-build/kpatch-funcs.sh | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 kpatch-build/kpatch-funcs.sh diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 996a866a..7fb7c2f3 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -39,6 +39,7 @@ set -o pipefail BASE="$PWD" SCRIPTDIR="$(readlink -f "$(dirname "$(type -p "$0")")")" +source "$SCRIPTDIR/kpatch-funcs.sh" ARCH="$(uname -m)" TARGET_ARCH="${TARGET_ARCH:-$ARCH}" CPUS="$(getconf _NPROCESSORS_ONLN)" @@ -1082,7 +1083,7 @@ else elif [[ "$DISTRO" = photon ]]; then mv "$RPMTOPDIR"/BUILD/linux-"$KVER" "$KERNEL_SRCDIR" 2>&1 | logger || die else - mv "$RPMTOPDIR"/BUILD/kernel-*/linux-* "$KERNEL_SRCDIR" 2>&1 | logger || die + find_rpm_linux_srcdir fi rm -rf "$RPMTOPDIR" diff --git a/kpatch-build/kpatch-funcs.sh b/kpatch-build/kpatch-funcs.sh new file mode 100644 index 00000000..7783d5ba --- /dev/null +++ b/kpatch-build/kpatch-funcs.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Shared helper functions for kpatch-build (and tests). +# +# Copyright (C) 2014 Seth Jennings +# Copyright (C) 2013,2014 Josh Poimboeuf +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, +# 02110-1301, USA. + +# Find and move the linux source directory from an RPM BUILD tree. +# Handles both traditional flat layout (Fedora < 42, RHEL, CentOS): +# BUILD/kernel-6.12.0/linux-6.12.0-100.fc41.x86_64/ +# and nested layout (Fedora 42+): +# BUILD/kernel-6.14.0-build/kernel-6.14/linux-6.14.0-63.fc42.x86_64/ +# +# Uses: RPMTOPDIR, KERNEL_SRCDIR (must be set by caller) +find_rpm_linux_srcdir() { + shopt -s nullglob + local dirs=( "$RPMTOPDIR"/BUILD/kernel-*/linux-* ) + shopt -u nullglob + + if [[ ${#dirs[@]} -eq 1 ]]; then + mv "${dirs[0]}" "$KERNEL_SRCDIR" 2>&1 | logger || die + elif [[ ${#dirs[@]} -gt 1 ]]; then + die "Multiple linux-* directories found in BUILD: ${dirs[*]}" + else + local found + found=$(find "$RPMTOPDIR/BUILD" -maxdepth 3 -type d -name "linux-*" \ + ! -path "*/configs/*") + if [[ -z "$found" ]]; then + die "Could not find linux source directory under $RPMTOPDIR/BUILD" + elif [[ $(echo "$found" | wc -l) -gt 1 ]]; then + die "Multiple linux source directories under $RPMTOPDIR/BUILD: $found" + else + mv "$found" "$KERNEL_SRCDIR" 2>&1 | logger || die + fi + fi +} From 02784afd2bebc36dfcc0c5188a8f591fa4d1753d Mon Sep 17 00:00:00 2001 From: Or Shoval Date: Wed, 25 Feb 2026 06:25:16 +0200 Subject: [PATCH 2/2] test: add unit tests for find_rpm_linux_srcdir Simulate RPM BUILD directory layouts across distros to validate the source extraction logic introduced in the previous commit: - Flat layout (Fedora < 42, RHEL 9, aarch64 variant) - Nested layout (Fedora 42+, aarch64 variant) - configs/ directory exclusion - Error cases: ambiguous matches, missing source, excessive depth Signed-off-by: Or Shoval Co-authored-by: Cursor --- test/unit/test-fedora-source-nesting.sh | 69 +++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 test/unit/test-fedora-source-nesting.sh diff --git a/test/unit/test-fedora-source-nesting.sh b/test/unit/test-fedora-source-nesting.sh new file mode 100755 index 00000000..c666dfcc --- /dev/null +++ b/test/unit/test-fedora-source-nesting.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# Test find_rpm_linux_srcdir() from kpatch-build. +# +# Verifies source directory detection across RPM BUILD layouts: +# Flat: BUILD/kernel-6.12.0/linux-6.12.0-100.fc41.x86_64/ (Fedora <42, RHEL, CentOS) +# Nested: BUILD/kernel-6.14.0-build/kernel-6.14/linux-6.14.0-63.fc42.x86_64/ (Fedora 42+) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" + +# Stub kpatch-build helpers so die doesn't exit and logger doesn't need a logfile. +die() { echo "die: $*" >&2; return 1; } +logger() { cat >/dev/null; } + +source "$SCRIPT_DIR/kpatch-build/kpatch-funcs.sh" + +TESTDIR=$(mktemp -d) +trap 'rm -rf "$TESTDIR"' EXIT +ERRORS=0 + +# Create a BUILD tree, run find_rpm_linux_srcdir, check result. +# $1 = test name +# $2 = expected: "ok" (source moved) or "fail" (die called) +# $3... = paths to create under BUILD/ +assert_layout() { + local name="$1" expect="$2"; shift 2 + local dir="$TESTDIR/$name" + local rc=0 + + mkdir -p "$dir/BUILD" "$dir/dest" + for p in "$@"; do mkdir -p "$dir/BUILD/$p"; done + + RPMTOPDIR="$dir" KERNEL_SRCDIR="$dir/dest/linux" \ + find_rpm_linux_srcdir 2>/dev/null || rc=$? + + case "$expect" in + ok) [[ $rc -eq 0 && -d "$dir/dest/linux" ]] || { echo "FAIL $name"; ((ERRORS++)); return; } ;; + fail) [[ $rc -ne 0 ]] || { echo "FAIL $name — expected error"; ((ERRORS++)); return; } ;; + esac + echo "ok $name" +} + +# Flat layout: Fedora 41 / RHEL / CentOS +assert_layout "flat-fc41-x86_64" ok "kernel-6.12.0/linux-6.12.0-100.fc41.x86_64" +assert_layout "flat-fc41-aarch64" ok "kernel-6.12.0/linux-6.12.0-100.fc41.aarch64" +assert_layout "flat-rhel9" ok "kernel-5.14.0/linux-5.14.0-362.el9.x86_64" + +# Nested layout: Fedora 42+ +assert_layout "nested-fc42" ok "kernel-6.14.0-build/kernel-6.14/linux-6.14.0-63.fc42.x86_64" +assert_layout "nested-fc42-arm" ok "kernel-6.14.0-build/kernel-6.14/linux-6.14.0-63.fc42.aarch64" + +# configs/linux-* dirs must be ignored by the nested search +assert_layout "nested-with-configs" ok "kernel-6.14.0-build/configs/linux-extra" \ + "kernel-6.14.0-build/kernel-6.14/linux-6.14.0-63.fc42.x86_64" + +# Error: ambiguous (multiple matches) +assert_layout "multi-flat" fail "kernel-6.14.0/linux-6.14.0-aaa" \ + "kernel-6.14.0/linux-6.14.0-bbb" +assert_layout "multi-nested" fail "kernel-6.14.0-build/kernel-6.14/linux-aaa" \ + "kernel-6.14.0-build/kernel-6.14/linux-bbb" + +# Error: nothing to find +assert_layout "empty-build" fail +assert_layout "no-linux-dir" fail "kernel-6.14.0-build/kernel-6.14/sources" +assert_layout "too-deep" fail "a/b/c/d/linux-6.14.0" + +echo "" +[[ $ERRORS -gt 0 ]] && { echo "$ERRORS test(s) failed"; exit 1; } +echo "All tests passed."