image_signing: ensure_secure_kernelparams.sh: use loopback devices for speed

Rather than read out the whole kernel partition just to dump the kernel
config, set the image up via a loopback device and read from there.

BRANCH=None
BUG=chromium:714598
TEST=signing images still works

Change-Id: I3797a0e77315e8baf6f481f31c44b889ac6d098a
Reviewed-on: https://chromium-review.googlesource.com/505475
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/image_signing/common.sh b/scripts/image_signing/common.sh
index e816a88..f100be5 100644
--- a/scripts/image_signing/common.sh
+++ b/scripts/image_signing/common.sh
@@ -68,6 +68,59 @@
   echo -e >&2   "${V_BOLD_RED}${CROS_LOG_PREFIX:-}ERROR  : $1${V_VIDOFF}"
 }
 
+TEMP_LOOP_LIST=$(mktemp)
+
+# Setup a loopback device for a file and scan for partitions, with retries.
+#
+# $1 - The file to back the new loopback device.
+# $2-$N - Additional arguments to pass to losetup.
+loopback_partscan() {
+  local lb_dev image="$1"
+  shift
+
+  # We know partition scanning & dev node creation can be racy with udev and
+  # the kernel, and the kernel does not sleep/wait for it to finish.  We have
+  # to use the partx tool manually as it will sleep until things are finished.
+  lb_dev=$(sudo losetup --show -f "$@" "${image}")
+
+  # Cache the path so we can clean it up.
+  echo "${lb_dev}" >>"${TEMP_LOOP_LIST}"
+
+  # Ignore problems deleting existing partitions. There shouldn't be any
+  # which will upset partx, but that's actually ok.
+  sudo partx -d "${lb_dev}" 2>/dev/null || true
+  sudo partx -a "${lb_dev}"
+
+  echo "${lb_dev}"
+}
+
+# Detach a loopback device set up earlier.
+#
+# $1 - The loop device to detach.
+# $2-$N - Additional arguments to pass to losetup.
+loopback_detach() {
+  # Retry the deletes before we detach.  crbug.com/469259
+  local i
+  for (( i = 0; i < 10; i++ )); do
+    if sudo partx -d "$1"; then
+      break
+    fi
+    warn "Sleeping & retrying ..."
+    sync
+    sleep 1
+  done
+  sudo losetup --detach "$@"
+}
+
+# Clear out all loopback devices we setup.
+cleanup_loopbacks() {
+  local line
+  while read -r line; do
+    loopback_detach "${line}" 2>/dev/null
+  done <"${TEMP_LOOP_LIST}"
+  rm -f "${TEMP_LOOP_LIST}"
+}
+
 # Usage: lsbval path-to-lsb-file key
 # Returns the value for the given lsb-release file variable.
 lsbval() {
@@ -96,3 +149,4 @@
 trap "cleanup" INT TERM EXIT
 
 add_cleanup_action "cleanup_temps_and_mounts"
+add_cleanup_action "cleanup_loopbacks"
diff --git a/scripts/image_signing/ensure_secure_kernelparams.sh b/scripts/image_signing/ensure_secure_kernelparams.sh
index 57e1036..daebe45 100755
--- a/scripts/image_signing/ensure_secure_kernelparams.sh
+++ b/scripts/image_signing/ensure_secure_kernelparams.sh
@@ -67,6 +67,8 @@
     local output
     # Copy of a string before it has been through sed
     local pre_sed
+    # Where the disk image is mounted.
+    local loopdev
 
     if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then
         usage
@@ -89,14 +91,18 @@
     # Either way, load test-expectations data from config.
     . "$configfile" || return 1
 
-    local kernelblob=$(make_temp_file)
+    # Set up the image on a loopback device so it's faster to access.
+    local loopdev
+    loopdev=$(loopback_partscan "${image}")
+
     # TODO(jimhebert): Perform the kernel security tests on both the kernel
     #                  partitions. Here, we just run it on kernel partition 4
     #                  which is the install kernel on the recovery image.
     #                  crosbug.com/24274
-    extract_image_partition "$image" 4 "$kernelblob"
+    local loop_kern="${loopdev}p4"
+    local loop_rootfs="${loopdev}p3"
     local rootfs=$(make_temp_dir)
-    mount_image_partition_ro "$image" 3 "$rootfs"
+    sudo mount -o ro "${loop_rootfs}" "${rootfs}"
 
     # Pick the right set of test-expectation data to use.
     local boardvar=$(get_boardvar_from_lsb_release "${rootfs}")
@@ -120,7 +126,7 @@
     output+="$(printf "\t'%s'\n" "${required_dmparams_regex[@]}")\n)\n"
 
     # Divide the dm params from the rest and process seperately.
-    local kparams=$(dump_kernel_config "$kernelblob")
+    local kparams=$(sudo dump_kernel_config "${loop_kern}")
     local dmparams=$(get_dmparams "$kparams")
     local kparams_nodm=$(kparams_remove_dm "$kparams")