Modular backuptool.sh.  Executes backup and restore methods defined in arbitrary /system/addon.d/*.sh scripts.

Tips & Tricks
=============
* 50-cm.sh contains only a reference implementation.  You may customize the methods however you wish.
  For example, 20-foobar.sh pre-backup can use a loop with conditionals to generate a dynamic backup list in
  /tmp/foobar_file_list which is later printed by list_files() so the backup method will act on those files.
* Optional methods pre-backup, post-backup, pre-restore, or post-restore may be defined for special purposes.
* Inject new files into /tmp/addon.d/ prior to backuptool.sh backup if you want to act during the current CM upgrade.
* Delete files from /tmp/addon.d/ during post-restore if you want to permanently remove files from /system/addon.d/
  Addons may use this approach to run a script only once.
* Scripts run in sort -n order.  Prefix with numbers 00 through 99 if want to run in a particular order.
* You can have two separate scripts, implementing only backup in one, and only restore in the other with a different
  number prefix of each.  This allows even greater control the backup/restore order even further.
* You could use pre-backup to generate a one-time use backup script in /tmp/addon.d/ that deletes itself in
  post-restore.

Patch Series
============
http://review.cyanogenmod.com/#change,13265
CyanogenMod/android_build
  * edify generator
http://review.cyanogenmod.com/#change,13266
CyanogenMod/android_system_core
  * permissions on /system/addon.d
http://review.cyanogenmod.com/#change,13267
CyanogenMod/android_vendor_cm
  * 50-cm.sh reference backup script
  * modular backuptool.sh
  * support backuptool.functions used by /system/addon.d/*.sh scripts

Change-Id: Ifd5eaf9dcfd68d92e5043c21d1bae1dc0ad54860
diff --git a/config/common.mk b/config/common.mk
index 57b81f8..03f6225 100644
--- a/config/common.mk
+++ b/config/common.mk
@@ -24,8 +24,11 @@
 PRODUCT_COPY_FILES += \
     vendor/cm/CHANGELOG.mkdn:system/etc/CHANGELOG-CM.txt
 
+# Backup Tool
 PRODUCT_COPY_FILES += \
-    vendor/cm/prebuilt/common/bin/backuptool.sh:system/bin/backuptool.sh
+    vendor/cm/prebuilt/common/bin/backuptool.sh:system/bin/backuptool.sh \
+    vendor/cm/prebuilt/common/bin/backuptool.functions:system/bin/backuptool.functions \
+    vendor/cm/prebuilt/common/bin/50-cm.sh:system/addon.d/50-cm.sh
 
 # init.d support
 PRODUCT_COPY_FILES += \
@@ -116,4 +119,4 @@
 
 PRODUCT_PROPERTY_OVERRIDES += \
   ro.cm.version=$(CM_VERSION) \
-  ro.modversion=$(CM_VERSION)
\ No newline at end of file
+  ro.modversion=$(CM_VERSION)
diff --git a/prebuilt/common/bin/50-cm.sh b/prebuilt/common/bin/50-cm.sh
new file mode 100755
index 0000000..24bfe33
--- /dev/null
+++ b/prebuilt/common/bin/50-cm.sh
@@ -0,0 +1,41 @@
+#!/sbin/sh
+# 
+# /system/addon.d/50-cm.sh
+# During a CM9 upgrade, this script backs up /system/etc/hosts,
+# /system is formatted and reinstalled, then the file is restored.
+#
+
+. /tmp/backuptool.functions
+
+list_files() {
+cat <<EOF
+etc/hosts
+EOF
+}
+
+case "$1" in
+  backup)
+    list_files | while read FILE DUMMY; do
+      backup_file $S/"$FILE"
+    done
+  ;;
+  restore)
+    list_files | while read FILE REPLACEMENT; do
+      R=""
+      [ -n "$REPLACEMENT" ] && R="$S/$REPLACEMENT"
+      [ -f "$C/$S/$FILE" ] && restore_file $S/"$FILE" "$R"
+    done
+  ;;
+  pre-backup)
+    # Stub
+  ;;
+  post-backup)
+    # Stub
+  ;;
+  pre-restore)
+    # Stub
+  ;;
+  post-restore)
+    # Stub
+  ;;
+esac
diff --git a/prebuilt/common/bin/backuptool.functions b/prebuilt/common/bin/backuptool.functions
new file mode 100644
index 0000000..833460f
--- /dev/null
+++ b/prebuilt/common/bin/backuptool.functions
@@ -0,0 +1,37 @@
+#!/sbin/sh
+#
+# Functions for backuptool.sh
+#
+
+export C=/tmp/backupdir
+export S=/system
+export V=9
+
+backup_file() {
+  if [ -e "$1" ]; then
+    local F=`basename "$1"`
+    local D=`dirname "$1"`
+    # dont backup any apps that have odex files, they are useless
+    if ( echo "$F" | grep -q "\.apk$" ) && [ -e `echo "$1" | sed -e 's/\.apk$/\.odex/'` ]; then
+      echo "Skipping odexed apk $1";
+    else
+      mkdir -p "$C/$D"
+      cp -p $1 "$C/$D/$F"
+    fi
+  fi
+}
+
+restore_file() {
+  local FILE=`basename "$1"`
+  local DIR=`dirname "$1"`
+  if [ -e "$C/$DIR/$FILE" ]; then
+    if [ ! -d "$DIR" ]; then
+      mkdir -p "$DIR";
+    fi
+    cp -p "$C/$DIR/$FILE" "$1";
+    if [ -n "$2" ]; then
+      echo "Deleting obsolete file $2"
+      rm "$2";
+    fi
+  fi
+}
diff --git a/prebuilt/common/bin/backuptool.sh b/prebuilt/common/bin/backuptool.sh
index 3b13843..fd62159 100755
--- a/prebuilt/common/bin/backuptool.sh
+++ b/prebuilt/common/bin/backuptool.sh
@@ -1,215 +1,80 @@
 #!/sbin/sh
 #
-# Backup and restore proprietary Android system files
+# Backup and restore addon /system files
 #
 
-C=/tmp/backupdir
-S=/system
-V=9
+export C=/tmp/backupdir
+export S=/system
+export V=9
 
-PROCEED=1;
+# Mount /system if it is not already mounted
+mount_system() {
+if [ ! -f "$S/build.prop" ]; then
+  mount $S
+fi
+}
 
+# Unmount /system unless it is already unmounted
+umount_system() {
+if [ -f "$S/build.prop" ]; then
+  umount $S
+fi
+}
+
+# Preserve /system/addon.d in /tmp/addon.d
+preserve_addon_d() {
+  mkdir -p /tmp/addon.d/
+  cp -a /system/addon.d/* /tmp/addon.d/
+  chmod 755 /tmp/addon.d/*.sh
+}
+
+# Restore /system/addon.d in /tmp/addon.d
+restore_addon_d() {
+  cp -a /tmp/addon.d/* /system/addon.d/
+  rm -rf /tmp/addon.d/
+}
+
+# Proceed only if /system is the expected major version
 check_prereq() {
-   if ( ! grep -q "^ro.cm.version=$V.*" /system/build.prop );
-   then
-      echo "Not backing up files from incompatible version.";
-      PROCEED=0;
-   fi
+if ( ! grep -q "^ro.cm.version=$V.*" /system/build.prop ); then
+  echo "Not backing up files from incompatible version."
+  umount_system
+  exit 127
+fi
 }
 
-check_installscript() {
-   if [ -f "/tmp/.installscript" ] && [ $PROCEED -ne 0 ];
-   then
-      # We have an install script, and ROM versions match!
-      # We now need to check and see if we have force_backup
-      # in either /etc or /tmp/backupdir 
-      if [ -f "$S/etc/force_backuptool" ] || [ -f "$C/force_backuptool" ];
-      then
-         echo "force_backuptool file found, Forcing backuptool."
-      else
-         echo "/tmp/.installscript found. Skipping backuptool."
-         PROCEED=0;
-      fi
-   fi
+# Execute /system/addon.d/*.sh scripts with $1 parameter
+run_stage() {
+for script in $(find /tmp/addon.d/ -name '*.sh' |sort -n); do
+  $script $1
+done
 }
 
-get_files() {
-    cat <<EOF
-vendor/pittpatt/models/recognition/face.face.y0-y0-22-b-N/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/left_eye-y0-yi45-p0-pi45-rn7-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/nose_base-y0-yi45-p0-pi45-r0-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/right_eye-y0-yi45-p0-pi45-rp7-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/nose_base-y0-yi45-p0-pi45-rp7-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/left_eye-y0-yi45-p0-pi45-rp7-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/right_eye-y0-yi45-p0-pi45-rn7-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/nose_base-y0-yi45-p0-pi45-rn7-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/right_eye-y0-yi45-p0-pi45-r0-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/multi_pose_face_landmark_detectors.3/left_eye-y0-yi45-p0-pi45-r0-ri20.2d_n2/full_model.bin
-vendor/pittpatt/models/detection/yaw_roll_face_detectors.3/head-y0-yi45-p0-pi45-rn30-ri30.5/full_model.bin
-vendor/pittpatt/models/detection/yaw_roll_face_detectors.3/head-y0-yi45-p0-pi45-rp30-ri30.5/full_model.bin
-vendor/pittpatt/models/detection/yaw_roll_face_detectors.3/head-y0-yi45-p0-pi45-r0-ri30.4a/full_model.bin
-framework/com.android.nfc_extras.jar
-framework/com.google.widevine.software.drm.jar
-framework/com.google.android.maps.jar
-framework/com.google.android.media.effects.jar
-lib/libfacelock_jni.so
-lib/libfilterpack_facedetect.so
-lib/libflint_engine_jni_api.so
-lib/libfrsdk.so
-lib/libgcomm_jni.so
-lib/libpicowrapper.so
-lib/libspeexresampler.so
-lib/libspeexwrapper.so
-lib/libvideochat_jni.so
-lib/libvideochat_stabilize.so
-lib/libvoicesearch.so
-etc/permissions/com.google.android.nfc_extras.xml
-etc/permissions/com.google.android.media.effects.xml
-etc/permissions/com.google.android.maps.xml
-etc/permissions/com.google.widevine.software.drm.xml
-etc/permissions/features.xml
-app/MediaUploader.apk
-app/GoogleFeedback.apk
-app/GoogleTTS.apk
-app/MarketUpdater.apk
-app/GoogleServicesFramework.apk
-app/YouTube.apk
-app/GenieWidget.apk
-app/GooglePackageVerifierUpdater.apk
-app/SetupWizard.apk app/Provision.apk
-app/GoogleEarth.apk
-app/ChromeBookmarksSyncAdapter.apk
-app/GoogleQuickSearchBox.apk app/QuickSearchBox.apk
-app/GoogleLoginService.apk
-app/Talk.apk
-app/Maps.apk
-app/GooglePackageVerifier.apk
-app/GoogleBackupTransport.apk
-app/GalleryGoogle.apk app/Gallery2.apk
-app/FaceLock.apk
-app/Vending.apk
-app/GoogleContactsSyncAdapter.apk
-app/GoogleCalendarSyncAdapter.apk
-app/Gmail.apk
-app/OneTimeInitializer.apk
-app/NetworkLocation.apk
-app/GooglePartnerSetup.apk
-app/Phonesky.apk
-app/VoiceSearch.apk
-etc/hosts
-etc/custom_backup_list.txt
-etc/force_backuptool
-EOF
-}
-
-get_custom_files() {
-   local L
-   if [ -f "$C/custom_backup_list.txt" ];
-   then
-      [ ! -f $C/fixed_custom_backup_list.txt ] && tr -d '\r' < $C/custom_backup_list.txt \
-            > $C/fixed_custom_backup_list.txt
-      L=`cat $C/fixed_custom_backup_list.txt`
-      cat <<EOF
-$L
-EOF
-   fi
-}
-
-backup_file() {
-   if [ -e "$1" ];
-   then
-      if [ -n "$2" ];
-      then
-         echo "$2  $1" | md5sum -c -
-         if [ $? -ne 0 ];
-         then
-            echo "MD5Sum check for $1 failed!";
-            exit $?;
-         fi
-      fi
-      
-      local F=`basename $1`
-      local D=`dirname $1`
-      
-      # dont backup any apps that have odex files, they are useless
-      if ( echo $F | grep -q "\.apk$" ) && [ -e `echo $1 | sed -e 's/\.apk$/\.odex/'` ];
-      then
-         echo "Skipping odexed apk $1";
-      else
-         mkdir -p $C/bak/$D
-         cp -p $1 $C/bak/$D/$F
-      fi
-   fi
-}
-
-restore_file() {
-   local FILE=`basename $1`
-   local DIR=`dirname $1`
-   if [ -e "$C/bak/$DIR/$FILE" ];
-   then
-      if [ ! -d "$DIR" ];
-      then
-         mkdir -p $DIR;
-      fi
-      cp -p $C/bak/$DIR/$FILE $1;
-      if [ -n "$2" ];
-      then
-         rm $2;
-      fi
-   fi
-}
-
-# don't (u)mount system if already done
-UMOUNT=0
-
 case "$1" in
-   backup)
-      if [ ! -f "$S/build.prop" ]; then
-         mount $S
-         UMOUNT=1
-      fi
-      check_prereq;
-      check_installscript;
-      if [ $PROCEED -ne 0 ];
-      then
-         rm -rf $C
-         mkdir -p $C
-         for file_list in get_files get_custom_files; do
-           $file_list | while read FILE REPLACEMENT; do
-              backup_file $S/$FILE
-           done
-         done
-      fi
-      if [ $UMOUNT -ne 0 ]; then
-         umount $S
-      fi
-   ;;
-   restore)
-      if [ ! -f "$S/build.prop" ]; then
-         mount $S
-         UMOUNT=1
-      fi
-      check_prereq;
-      check_installscript;
-      if [ $PROCEED -ne 0 ];
-      then
-         for file_list in get_files get_custom_files; do
-           $file_list | while read FILE REPLACEMENT; do
-              R=""
-              [ -n "$REPLACEMENT" ] && R="$S/$REPLACEMENT"
-              restore_file $S/$FILE $R
-           done
-         done
-         rm -rf $C
-      fi
-      if [ $UMOUNT -ne 0 ]; then
-         umount $S
-      fi
-      sync
-   ;;
-   *)
-      echo "Usage: $0 {backup|restore}"
-      exit 1
+  backup)
+    mkdir -p $C
+    mount_system
+    check_prereq
+    preserve_addon_d
+    run_stage pre-backup
+    run_stage backup
+    run_stage post-backup
+    umount_system
+  ;;
+  restore)
+    mount_system
+    check_prereq
+    run_stage pre-restore
+    run_stage restore
+    run_stage post-restore
+    restore_addon_d
+    umount_system
+    rm -rf $C
+    sync
+  ;;
+  *)
+    echo "Usage: $0 {backup|restore}"
+    exit 1
 esac
 
 exit 0