Copy logic to preserve CRX files from clobber-state.

BUG=chrome-os-partner:25069
TEST=Manually create an extension cache, factory_reset, ensure that \
  the extension cache remains

Change-Id: I021fb1d616e0d60724187d268410234706125d7c
Reviewed-on: https://chromium-review.googlesource.com/182772
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Commit-Queue: Jon Salz <jsalz@chromium.org>
Tested-by: Jon Salz <jsalz@chromium.org>
(cherry picked from commit d1715d0b377a0b4dce628ef99080485b98feb489)
Reviewed-on: https://chromium-review.googlesource.com/182765
Reviewed-by: Bowgo Tsai <bowgotsai@chromium.org>
diff --git a/factory_reset.sh b/factory_reset.sh
index faa0f1f..0d13399 100644
--- a/factory_reset.sh
+++ b/factory_reset.sh
@@ -7,6 +7,72 @@
 # This runs from the factory install/reset shim. This MUST be run
 # from USB, in developer mode. This script will wipe OQC activity and
 # put the system back into factory fresh/shippable state.
+
+# Preserve files in the CRX cache.  Largely copied from clobber-state.
+# TODO(dgarrett,jsalz): Consolidate.
+#
+# Note that some variables are shared with restore_crx_cache.
+preserve_crx_cache() {
+  PRESERVED_TAR="/tmp/preserve.tar"
+  PRESERVED_FILES=""
+  STATE_PATH="/mnt/stateful_partition"
+  mkdir -p $STATE_PATH
+  mount $STATE_DEV $STATE_PATH
+  IMPORT_FILES="$(cd ${STATE_PATH};
+                  echo unencrypted/import_extensions/extensions/*.crx)"
+  if [ "$IMPORT_FILES" != \
+       "unencrypted/import_extensions/extensions/*.crx" ]; then
+    PRESERVED_FILES="${PRESERVED_FILES} ${IMPORT_FILES}"
+  fi
+  PRESERVED_LIST=""
+  if [ -n "$PRESERVED_FILES" ]; then
+    # We want to preserve permissions and recreate the directory structure
+    # for all of the files in the PRESERVED_FILES variable. In order to do
+    # so we run tar --no-recurison and specify the names of each of the
+    # parent directories. For example for home/.shadow/install_attributes.pb
+    # we pass to tar home home/.shadow home/.shadow/install_attributes.pb
+    for file in $PRESERVED_FILES; do
+      if [ ! -e "$STATE_PATH/$file" ]; then
+        continue
+      fi
+      path=$file
+      while [ "$path" != '.' ]; do
+        PRESERVED_LIST="$path $PRESERVED_LIST"
+        path=$(dirname $path)
+      done
+    done
+    tar cf $PRESERVED_TAR -C $STATE_PATH --no-recursion -- $PRESERVED_LIST
+  fi
+  # Try a few times to unmount the stateful partition.
+  local unmounted=false
+  for i in $(seq 5); do
+    if umount $STATE_DEV; then
+      unmounted=true
+      break
+    fi
+    sleep 1
+  done
+  if ! $unmounted; then
+    # Bail out: we may not be able to successfully mkfs.
+    echo "Unable to unmount stateful partition. Aborting."
+    exit 1
+  fi
+}
+
+# Restores files previously preserved by preserve_crx_cache.
+restore_crx_cache() {
+  if [ -n "$PRESERVED_LIST" ]; then
+    # Copy files back to stateful partition
+    mount $STATE_DEV $STATE_PATH
+    tar xfp $PRESERVED_TAR -C $STATE_PATH
+    sync  # Try as best we can, in case umount fails
+    umount $STATE_PATH
+    # Sleep for a bit, since we will shut down soon and want to give
+    # the drive a chance to flush everything
+    sleep 3
+  fi
+}
+
 echo "Factory reset"
 
 # TODO(crosbug:10680): replace arch detection with crossystem?
@@ -50,10 +116,15 @@
     echo "Failed to find stateful partition."
     exit 1
   fi
+
+  preserve_crx_cache
+
   # Just wipe the start of the partition and remake the fs on
   # the stateful partition.
   dd bs=4M count=1 if=/dev/zero of=${STATE_DEV}
   /sbin/mkfs.ext4 "$STATE_DEV"
+
+  restore_crx_cache
 fi
 
 # Do any board specific resetting here.