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>
This commit is contained in:
Mike Frysinger 2011-09-28 11:59:53 -04:00 committed by Gerrit
parent 23301afb5f
commit 286b5928c8
2 changed files with 73 additions and 42 deletions

View File

@ -378,6 +378,36 @@ function sudo_append() {
sudo tee -a "$1" > /dev/null
}
# Execute multiple commands in a single sudo. Generally will speed things
# up by avoiding multiple calls to `sudo`. If any commands fail, we will
# call die with the failing command. We can handle a max of ~100 commands,
# but hopefully no one will ever try that many at once.
#
# $@ - The commands to execute, one per arg.
function sudo_multi() {
local i cmds
# Construct the shell code to execute. It'll be of the form:
# ... && ( ( command ) || exit <command index> ) && ...
# This way we know which command exited. The exit status of
# the underlying command is lost, but we never cared about it
# in the first place (other than it is non zero), so oh well.
for (( i = 1; i <= $#; ++i )); do
cmds+=" && ( ( ${!i} ) || exit $(( i + 10 )) )"
done
# Execute our constructed shell code.
sudo -- sh -c ":${cmds[*]}" && i=0 || i=$?
# See if this failed, and if so, print out the failing command.
if [[ $i -gt 10 ]]; then
: $(( i -= 10 ))
die "sudo_multi failed: ${!i}"
elif [[ $i -ne 0 ]]; then
die "sudo_multi failed for unknown reason $i"
fi
}
# Locate all mounts below a specified directory.
#
# $1 - The root tree.

View File

@ -87,13 +87,16 @@ SYNCERPIDFILE="${FLAGS_chroot}/var/tmp/enter_chroot_sync.pid"
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 @@ function ensure_mounted {
# 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 @@ function setup_env {
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 @@ function setup_env {
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 @@ function setup_env {
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 @@ function setup_env {
# 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.