enter_chroot: merge multiple mount requests into a single sudo

Every invocation of `sudo` delays things, so merge all of the mounts
into a single `sudo` command when possible.  This saves over 1 second
on initial execution (out of ~4 seconds total).

BUG=None
TEST=`cros_sdk --enter true` still mounts & unmounts properly

Change-Id: Ibc66507dc21250a81207d2f940645eaebe93c79c
Reviewed-on: https://gerrit.chromium.org/gerrit/10901
Reviewed-by: David James <davidjames@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Commit-Ready: Mike Frysinger <vapier@chromium.org>
diff --git a/sdk_lib/enter_chroot.sh b/sdk_lib/enter_chroot.sh
index 377e565..26df65b 100755
--- a/sdk_lib/enter_chroot.sh
+++ b/sdk_lib/enter_chroot.sh
@@ -87,13 +87,16 @@
 
 
 MOUNTED_PATH=$(readlink -f "$FLAGS_chroot")
-function ensure_mounted {
+function mount_queue_init {
+  MOUNT_QUEUE=()
+}
+
+function queue_mount {
   # If necessary, mount $source in the host FS at $target inside the
   # chroot directory with $mount_args.
   local source="$1"
   local mount_args="$2"
   local target="$3"
-  local warn="$4"
 
   local mounted_path="${MOUNTED_PATH}$target"
 
@@ -102,29 +105,23 @@
     # Already mounted!
     ;;
   *)
-    # Attempt to make the mountpoint as the user.  This depends on the
-    # fact that all mountpoints that should be owned by root are
-    # already present.  However, when distributions add new paths (such as
-    # Ubuntu 11.10's /run), it might not yet exist in the chroot.  If that
-    # happens, just create it as root.
-    if ! mkdir -p "${mounted_path}" 2>/dev/null; then
-      sudo mkdir -p "${mounted_path}"
-    fi
-
-    # NB:  mount_args deliberately left unquoted
-    debug mount ${mount_args} "${source}" "${mounted_path}"
-    if ! sudo -- mount ${mount_args} "${source}" "${mounted_path}" ; then
-      if [ -z "${warn}" ]; then
-        die "Could not mount ${source} on ${mounted_path}"
-      else
-        warn "Failed to mount ${source}; perhaps it's on NFS?"
-        warn "${warn}"
-      fi
-    fi
+    MOUNT_QUEUE+=(
+      "mkdir -p '${mounted_path}'"
+      # The args are left unquoted on purpose.
+      "mount ${mount_args} '${source}' '${mounted_path}'"
+    )
     ;;
   esac
 }
 
+function process_mounts {
+  if [[ ${#MOUNT_QUEUE[@]} -eq 0 ]]; then
+    return 0
+  fi
+  sudo_multi "${MOUNT_QUEUE[@]}"
+  mount_queue_init
+}
+
 function env_sync_proc {
   # This function runs and performs periodic updates to the chroot env, if
   # necessary.
@@ -238,17 +235,18 @@
 
     debug "Mounting chroot environment."
     MOUNT_CACHE=$(mount)
-    ensure_mounted none "-t proc" /proc
-    ensure_mounted none "-t sysfs" /sys
-    ensure_mounted /dev "--bind" /dev
-    ensure_mounted none "-t devpts" /dev/pts
+    mount_queue_init
+    queue_mount none "-t proc" /proc
+    queue_mount none "-t sysfs" /sys
+    queue_mount /dev "--bind" /dev
+    queue_mount none "-t devpts" /dev/pts
     if [ -d /run ]; then
-      ensure_mounted /run "--bind" /run
+      queue_mount /run "--bind" /run
       if [ -d /run/shm ]; then
-        ensure_mounted /run/shm "--bind" /run/shm
+        queue_mount /run/shm "--bind" /run/shm
       fi
     fi
-    ensure_mounted "${FLAGS_trunk}" "--bind" "${CHROOT_TRUNK_DIR}"
+    queue_mount "${FLAGS_trunk}" "--bind" "${CHROOT_TRUNK_DIR}"
 
     if [ $FLAGS_ssh_agent -eq $FLAGS_TRUE ]; then
       if [ -n "${SSH_AUTH_SOCK}" -a -d "${HOME}/.ssh" ]; then
@@ -258,10 +256,24 @@
         cp "${HOME}"/.ssh/{known_hosts,*.pub} "${TARGET_DIR}/" 2>/dev/null || :
         copy_ssh_config "${TARGET_DIR}"
         ASOCK=${SSH_AUTH_SOCK%/*}
-        ensure_mounted "${ASOCK}" "--bind" "${ASOCK}"
+        queue_mount "${ASOCK}" "--bind" "${ASOCK}"
       fi
     fi
 
+    if [ -d "$HOME/.subversion" ]; then
+      TARGET="/home/${USER}/.subversion"
+      mkdir -p "${FLAGS_chroot}${TARGET}"
+      queue_mount "${HOME}/.subversion" "--bind" "${TARGET}"
+    fi
+
+    if DEPOT_TOOLS=$(type -P gclient) ; then
+      DEPOT_TOOLS=${DEPOT_TOOLS%/*} # dirname
+      debug "Mounting depot_tools"
+      queue_mount "$DEPOT_TOOLS" --bind "$INNER_DEPOT_TOOLS_ROOT"
+    fi
+
+    process_mounts
+
     CHROME_ROOT="$(readlink -f "$FLAGS_chrome_root" || :)"
     if [ -z "$CHROME_ROOT" ]; then
       CHROME_ROOT="$(cat "${FLAGS_chroot}${CHROME_ROOT_CONFIG}" \
@@ -279,16 +291,11 @@
         debug "Mounting chrome source at: $INNER_CHROME_ROOT"
         sudo bash -c "echo '$CHROME_ROOT' > \
           '${FLAGS_chroot}${CHROME_ROOT_CONFIG}'"
-        ensure_mounted "$CHROME_ROOT" --bind "$INNER_CHROME_ROOT"
+        queue_mount "$CHROME_ROOT" --bind "$INNER_CHROME_ROOT"
       fi
     fi
 
-    if DEPOT_TOOLS=$(type -P gclient) ; then
-      DEPOT_TOOLS=${DEPOT_TOOLS%/*} # dirname
-      debug "Mounting depot_tools"
-      ensure_mounted "$DEPOT_TOOLS" --bind "$INNER_DEPOT_TOOLS_ROOT" \
-        "This may impact chromium build."
-    fi
+    process_mounts
 
     # Install fuse module.  Skip modprobe when possible for slight
     # speed increase when initializing the env.
@@ -313,12 +320,6 @@
     # Always write the temp file so we can read it when exiting
     echo "${SAVED_PREF:-false}" > "${FLAGS_chroot}${SAVED_AUTOMOUNT_PREF_FILE}"
 
-    if [ -d "$HOME/.subversion" ]; then
-      TARGET="/home/${USER}/.subversion"
-      mkdir -p "${FLAGS_chroot}${TARGET}"
-      ensure_mounted "${HOME}/.subversion" "--bind" "${TARGET}"
-    fi
-
     # Configure committer username and email in chroot .gitconfig.  Change
     # to the root directory first so that random $PWD/.git/config settings
     # do not get picked up.  We want to stick to ~/.gitconfig only.