fix_patches() refreshes each patch on its own against the original
source: it applies the patch, copies the patched files, and reverts it.
If a later patch in the series depends on an earlier one, it doesn't
apply to the original source, and klp-build fails even though
validate_patches(), which applies the patches in order, accepted the
series:

  $ ./scripts/livepatch/klp-build 0001-dummy.patch 0002-Revert-dummy.patch
  Validating patch(es)
  Building original kernel
  Copying original object files
  Fixing patch(es)
  Reversed (or previously applied) patch detected!  Assume -R? [n]
  Apply anyway? [n]
  1 out of 1 hunk ignored
  error: klp-build: .../klp-tmp/tmp.patch did not apply

Refreshing each patch on top of the previous ones wouldn't work either.
fix-patch-lines adds #line directives to each fixed patch, so the next
patch's context wouldn't match.  The #line numbers in later patches
would also refer to the intermediate source rather than the original
source, which defeats the purpose of preserving __LINE__.

Instead, apply all the patches in order and diff the result against the
original source to get a single combined patch, then run fix-patch-lines
on that.  If the patches have no net effect, fail early with "no changes
detected" instead of building the patched kernel for nothing.

Fixes: 24ebfcd65a87 ("livepatch/klp-build: Introduce klp-build script for 
generating livepatch modules")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <[email protected]>
---
 scripts/livepatch/klp-build | 93 +++++++++++++++++++++----------------
 1 file changed, 54 insertions(+), 39 deletions(-)

diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index b52a8489d9f6..6cdc36e97c21 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -467,60 +467,75 @@ do_init() {
        set_kernelversion
 }
 
-# Refresh the patch hunk headers, specifically the line numbers and counts.
-refresh_patch() {
-       local patch="$1"
+# Combine the patches into a single patch against the original source, with
+# refreshed hunk headers.  The patches may depend on each other, so they can't
+# be refreshed individually.
+combine_patches() {
+       local outfile="$1"
        local tmpdir="$PATCH_TMP_DIR"
-       local input_files=()
-       local output_files=()
+       local files=()
+       local patch
+       local file
 
        rm -rf "$tmpdir"
-       mkdir -p "$tmpdir/a"
-       mkdir -p "$tmpdir/b"
+       mkdir -p "$tmpdir"
 
-       # Get all source files affected by the patch
-       get_patch_input_files "$patch" | mapfile -t input_files
-       get_patch_output_files "$patch" | mapfile -t output_files
+       # Get all source files affected by the patches
+       for patch in "${PATCHES[@]}"; do
+               get_patch_files "$patch"
+       done | sort -u | mapfile -t files
 
-       # Copy orig source files to 'a'
-       echo "${input_files[@]}" | xargs cp --parents 
--target-directory="$tmpdir/a"
+       # Copy orig source files to $tmpdir
+       for file in "${files[@]}"; do
+               if [[ -e "$file" ]]; then
+                       cp --parents "$file" "$tmpdir"
+               fi
+       done
 
-       # Copy patched source files to 'b'
-       apply_patch "$patch" "--silent"
-       echo "${output_files[@]}" | xargs cp --parents 
--target-directory="$tmpdir/b"
-       revert_patch "$patch"
+       # Diff the orig source files against the patched ones in place to make a
+       # clean patch.  Use /dev/null for created and deleted files.
+       : > "$outfile"
+       apply_patches "--silent"
+       for file in "${files[@]}"; do
+               local old="$tmpdir/$file"
+               local new="$file"
+               local old_label="a/$file"
+               local new_label="b/$file"
+               local status=0
+
+               if [[ ! -e "$old" ]]; then
+                       old=/dev/null
+                       old_label=/dev/null
+               fi
+               if [[ ! -e "$new" ]]; then
+                       new=/dev/null
+                       new_label=/dev/null
+               fi
 
-       # Diff 'a' and 'b' to make a clean patch
-       ( cd "$tmpdir" && diff -Nupr a b > "$patch" ) || true
+               diff -up --label "$old_label" --label "$new_label" "$old" 
"$new" >> "$outfile" || status=$?
+               if (( status > 1 )); then
+                       die "diff failed for $file"
+               fi
+       done
+       revert_patches
 }
 
-# Copy the patches to a temporary directory, fix their lines so as not to
-# affect the __LINE__ macro for otherwise unchanged functions further down the
-# file, and update $PATCHES to point to the fixed patches.
+# Combine the patches into a single patch, fix its lines so as not to affect
+# the __LINE__ macro for otherwise unchanged functions further down the file,
+# and update $PATCHES to point to the fixed patch.
 fix_patches() {
-       local idx
-       local i
+       local tmp_patch="$TMP_DIR/tmp.patch"
+       local new_patch="$TMP_DIR/fixed.patch"
 
        rm -f "$TMP_DIR"/*.patch
 
-       idx=0001
-       for i in "${!PATCHES[@]}"; do
-               local old_patch="${PATCHES[$i]}"
-               local tmp_patch="$TMP_DIR/tmp.patch"
-               local patch="${PATCHES[$i]}"
-               local new_patch
-
-               new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")"
+       combine_patches "$tmp_patch"
+       [[ ! -s "$tmp_patch" ]] && die "no changes detected"
 
-               cp -f "$old_patch" "$tmp_patch"
-               refresh_patch "$tmp_patch"
-               "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$new_patch"
+       "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$new_patch"
+       PATCHES=("$new_patch")
 
-               PATCHES[i]="$new_patch"
-
-               rm -f "$tmp_patch"
-               idx=$(printf "%04d" $(( 10#$idx + 1 )))
-       done
+       rm -f "$tmp_patch"
 }
 
 clean_kernel() {
-- 
2.53.0-Meta


Reply via email to