#! /bin/sh -e # Script to handle kernel patches intelligently. usage() { echo Usage: lkpatch [--keep-broken] [--depends-only] [--optional-depends] [directory] [patch...] >&2 echo Takes one or more patches, and applies them to the kernel in the >&2 echo specified directory \(or current working directory if none given\). >&2 echo >&2 echo If patch is not specified, stdin is used. Otherwise, if the patch >&2 echo contains a Depends: line, apply those first, if not already applied. >&2 exit 1 } # Takes source dir, clone dir, and file list. clone_tree() { SRC=$1 DST=$2 shift 2 for f; do [ -d `dirname $DST/$f` ] || mkdir -p `dirname $DST/$f` ! [ -f $SRC/$f ] || cp $SRC/$f $DST/$f done } # Copy files back. If they don't exist, erase in master tree. # Takes source dir, dest dir, and file list copy_back() { SRC=$1 DST=$2 shift 2 for f; do # Break hard links! rm -f $DST/$f if [ -f $SRC/$f ]; then # Little-known fact: patch can create directories. DIR=$(dirname $DST/$f) [ -d "$DIR" ] || mkdir -p "$DIR" cp $SRC/$f $DST/$f fi done } # Find dependency $1 in dir $2. find_dep() { find_dep_file="$1" find_dep_dir="$2" # Absolute file path. case "$find_dep_file" in /*) echo "$find_dep_file"; return;; esac # For every dir in file, take one dir off directory. while [ "$(dirname $find_dep_file)" != . ]; do find_dep_dir="$(dirname $find_dep_dir)" find_dep_file="$(dirname $find_dep_file)" done # List all variants from most recent backwards. find_dep_wildcard=$(echo "$find_dep_dir"/"$1" | sed 's/gz$//') ls -t "$find_dep_wildcard"*gz } # Takes source directory, and list of dependencies... do_depends() { DEP_SOURCE_DIR=$1 shift for dep; do dep_files=`find_dep $dep $DEPDIR` if [ x"$dep_files" = x ]; then echo $0 error: could not find dependency "$dep" >&2 [ $OPTIONAL_DEPENDS = 1 ] || exit 1 else for dep_possible in $dep_files; do if isapplied $DEP_SOURCE_DIR $dep_possible >/dev/null || $0 $DEP_SOURCE_DIR $dep_possible then continue 2 fi done echo $0 error: Could not apply dependency "$dep". >&2 exit 1 fi done } KEEP_BROKEN=0 DEPENDS_ONLY=0 OPTIONAL_DEPENDS=0 while true; do case "$1" in --keep-broken) KEEP_BROKEN=1;; --depends-only) DEPENDS_ONLY=1;; --optional-depends) OPTIONAL_DEPENDS=1;; --) shift; break;; -*) echo Unknown argument "$1" >&2 usage ;; *) # No more args break; esac shift done # Set KDIR to canonical (no trailing / or /. allowed) case "$1" in /*) KDIR="$1";; *) KDIR="$(pwd)/$1";; esac KDIR=$(echo "$KDIR" | sed -e 's:\(/*\.\)*/*$::') [ x"$1" = x ] || shift # Set bogus stdin argument if no args. if [ x"$1" = x"" ]; then set -- ""; fi [ -d "$KDIR" ] || usage TMPFILE=`mktemp /tmp/$$.XXXXXX` trap "rm -rf $TMPFILE $KDIR.tmp.$$" EXIT STARTDIR=$(pwd) for PATCH; do if [ x"$PATCH" = x ]; then DEPDIR=""; else # Canonicalize relative paths. case "$PATCH" in /*) ;; *) PATCH="$STARTDIR/$PATCH";; esac DEPDIR=$(dirname $PATCH) fi case "$PATCH" in *.bz2) CAT=bzcat;; *.gz) CAT=zcat;; *) CAT=cat;; esac # Do Depends, if any [ x"$DEPDIR" = x ] || do_depends $KDIR $($CAT $PATCH | grep '^Depends:' | cut -d: -f2-) [ x"$DEPENDS_ONLY" = x0 ] || continue if [ x"$PATCH" = x ]; then # Don't know what's coming: copy whole tree. cp -al $KDIR $KDIR.tmp.$$ else mkdir $KDIR.tmp.$$ FILELIST=$($CAT $PATCH | grep '^+++ ' | cut -d/ -f2- | cut -d' ' -f1) clone_tree $KDIR $KDIR.tmp.$$ $FILELIST fi if $CAT $PATCH | (cd $KDIR.tmp.$$ && patch -s -p1); then : else if [ $KEEP_BROKEN -eq 1 ]; then if [ x"$PATCH" = x ]; then # Copy back those with linkcount == 1. FILELIST=$(cd $KDIR.tmp.$$ && find . -type f -links 1) fi copy_back $KDIR.tmp.$$/ $KDIR $FILELIST $(for f in $FILELIST; do echo $f.rej; done) fi exit 1 fi if [ x"$PATCH" = x ]; then # Copy back those with linkcount == 1. FILELIST=$(cd $KDIR.tmp.$$ && find . -type f -links 1) fi copy_back $KDIR.tmp.$$/ $KDIR $FILELIST rm -r $KDIR.tmp.$$ echo Applied $PATCH... done exit 0