diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index e7cc89c47..08d52542c 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -47,7 +47,7 @@ chrootcmd() { # env may be overridden when running in the background; don't let it fork. local ret=0 oldtrap="$TRAP" TRAP='' - env -i container="$container" chroot "$CHROOT" su -s '/bin/sh' -c "$*" - root || ret=$? + env -i chroot "$CHROOT" su -s '/bin/sh' -c "$*" - root || ret=$? local pid="$!" # $pid might not be set if env has not been redefined yet if [ -n "$BACKGROUND" ] && [ -n "$pid" ]; then @@ -323,7 +323,32 @@ bindmount() { fi mkdir -p "$target" mount --bind $bindopts "$1" "$target" - if [ -n "$3" ]; then + # Determine whether we really need to remount or not by checking the existing + # mount options. We are doing this since remount is not allowed in containers. + if [ -n "$3" ] && ! checkmountopt "$target" "$3"; then + mount -i -o "remount,$3" "$target" + fi +} + +# Recursively bind-mounts $1 into $CHROOT/${2:-"$1"} if $2 is not already mounted +# If $3 is specified, remounts with the specified options. +# If $1 starts with a -, it's considered options to the bind mount, and the rest +# of the parameters are shifted. +rbindmount() { + bindopts='' + if [ ! "${1#"-"}" = "$1" ]; then + bindopts="$1" + shift + fi + local target="`fixabslinks "${2:-"$1"}"`" + if mountpoint -q "$target"; then + return 0 + fi + mkdir -p "$target" + mount --rbind $bindopts "$1" "$target" + # Determine whether we really need to remount or not by checking the existing + # mount options. We are doing this since remount is not allowed in containers. + if [ -n "$3" ] && ! checkmountopt "$target" "$3"; then mount -i -o "remount,$3" "$target" fi } @@ -357,35 +382,18 @@ else firstrun='y' fi -if [ -n "$container" ]; then - devmknod null - devmknod full - devmknod zero - devmknod tty - devmknod random - devmknod urandom - if [ ! -d "$CHROOT/dev/pts" ]; then - mkdir -p "$CHROOT/dev/pts" - fi - mount -t devpts devpts -o newinstance,ptmxmode=0666,mode=0620,gid=5 "$CHROOT/dev/pts" - ln -sf pts/ptmx "$CHROOT/dev/ptmx" -else - bindmount /dev -fi -bindmount /dev/pts -bindmount /dev/shm -if [ -n "$container" ]; then - bindmount /tmp /tmp -else - bindmount /tmp /tmp exec -fi +rbindmount /dev +bindmount /tmp /tmp exec bindmount /proc tmpfsmount /var/run 'noexec,nosuid,mode=0755,size=10%' tmpfsmount /var/run/lock 'noexec,nosuid,nodev,size=5120k' bindmount /var/run/dbus /var/host/dbus bindmount /var/run/shill /var/host/shill bindmount /var/lib/timezone /var/host/timezone -bindmount /var/run/crouton-ext +# If findnacld socket exists, bind-mount it into chroot. +if [ -S "/var/run/crouton-ext/socket" ]; then + bindmount /var/run/crouton-ext +fi for m in /lib/modules/*; do if [ -d "$m" ]; then bindmount '-o ro' "$m" @@ -662,20 +670,25 @@ if [ ! "$NOLOGIN" = 1 ] && grep -q '^root:' "$passwd" 2>/dev/null; then # Launch systemd-logind if available and not already running # Whitelisted for saucy and trusty systemd_dir="`fixabslinks '/run/systemd'`" - if [ -z "$container" ] && [ -x "$CHROOT/lib/systemd/systemd-logind" ] && \ + if [ -x "$CHROOT/lib/systemd/systemd-logind" ] && \ [ ! -d "$systemd_dir" ] && \ [ "$CHROOTRELEASE" = 'saucy' -o "$CHROOTRELEASE" = 'trusty' ]; then # Every piece of systemd code ever assumes that this directory exists mkdir -p "$systemd_dir" # Create systemd cgroup if necessary + start_systemd=true if ! mountpoint -q "$CHROOT/sys/fs/cgroup/systemd"; then - mkdir -p "$CHROOT/sys/fs/cgroup/systemd" - mount -t cgroup -o nosuid,noexec,nodev,none,name=systemd systemd \ - "$CHROOT/sys/fs/cgroup/systemd" + if ! mkdir -p "$CHROOT/sys/fs/cgroup/systemd" || \ + ! mount -t cgroup -o nosuid,noexec,nodev,none,name=systemd systemd \ + "$CHROOT/sys/fs/cgroup/systemd"; then + start_systemd=false + fi + fi + # We cannot create/mount cgroup dirs in containers, so don't start systemd. + if $start_systemd; then + chrootcmd "/lib/systemd/systemd-logind >/dev/null 2>&1 /dev/null 2>&1 /dev/null; then addtrap "(echo $$ >> '$CROUTONLOCKDIR/frecon') & :" fi - # findnacld should have been started outside the container, so we don't need - # to start it again. - if [ -z "$container" ]; then + # If the socket file does not exist, it means findnacld is not started. + if [ ! -S "/var/run/crouton-ext/socket" ]; then chrootcmd "/usr/local/bin/croutonfindnacld >/dev/null 2>&1 &2 fi + # If there is no process running under $CHROOTSRC and we cannot + # unmount, then use lazy unmount. + # FIXME: This is a hack for running in containers, since we cannot + # unmount recursive bindmount in it. + arg='-l' for root in /proc/*/root; do if [ ! -r "$root" ] \ || [ ! "`readlink -f "$root"`" = "$base" ]; then @@ -285,6 +286,7 @@ unmount() { if [ -z "$printonly" ]; then kill "-$SIGNAL" "$pid" 2>/dev/null || true fi + arg='' done # Escalate @@ -326,10 +328,12 @@ done # Re-disable USB persistence (the Chromium OS default) if we no longer # have chroots running with a root in removable media -if [ -z "$container" ] && checkusage "$ROOT/media"; then +if checkusage "$ROOT/media"; then for usbp in /sys/bus/usb/devices/*/power/persist; do if [ -e "$usbp" ]; then - echo 0 > "$usbp" + # Don't fail since we don't have permission to write /sys/* if we are in + # container. + echo 0 > "$usbp" || true fi done fi diff --git a/installer/functions b/installer/functions index f903078f5..1938916d1 100644 --- a/installer/functions +++ b/installer/functions @@ -68,6 +68,37 @@ undotrap() { settrap "$TRAP" } +# Check if we are able to mknod. +capmknod() { + local tmp=`mktemp -d --tmpdir=/tmp 'crouton-mknod.XXX'` + if ! mknod "$tmp/test-mknod" c 0 0; then + return 1 + fi + rm -rf "$tmp" + return 0 +} + +# Check whether $1 is mounted with options $2. +# We check the options of a mount point by inspecting /proc/mounts. +# For options dev, suid and exec, since they will not show up in /proc/mounts, +# we check whether their opposite options (nodev, nosuid, noexec) exists. +checkmountopt() { + for opt in ${2//,/ }; do + case $opt in + dev|suid|exec) + if ! awk -v dir="$1" -v opt="no$opt(,|$)" \ + '$2 == dir && $4 ~ opt { exit 1 }' "/proc/mounts"; then + return 1 + fi;; + *) + if ! awk -v dir="$1" -v opt="$opt(,|$)" \ + '$2 == dir && $4 !~ opt { exit 1 }' "/proc/mounts"; then + return 1 + fi;; + esac + done +} + # Works mostly like built-in getopts but silently coalesces positional arguments. # Does not take parameters. Set getopts_string prior to calling. # Sets getopts_var and getopts_arg. @@ -156,9 +187,9 @@ release() { # sure the kernel does not panic (this is the default configuration of a vanilla # kernel). See crbug.com/260955 for details. disablehungtask() { - if [ -z "$container" ]; then - echo 0 > /proc/sys/kernel/hung_task_panic - fi + # Don't fail since we do not have permission to write /proc/sys/* if we are + # in container. + echo 0 > /proc/sys/kernel/hung_task_panic || true } # Run an awk program, without buffering its output. diff --git a/installer/ubuntu/bootstrap b/installer/ubuntu/bootstrap index fb8c405f9..725d8ef88 100644 --- a/installer/ubuntu/bootstrap +++ b/installer/ubuntu/bootstrap @@ -31,7 +31,7 @@ Check your internet connection or proxy settings and try again.' fi fi -# Patch debootstrap so that it retries downloading packages +# Patch debootstrap so that it retries downloading packages and not mounting /sys echo 'Patching debootstrap...' 1>&2 if awk ' t == 3 && /warning RETRY/ { print "sleep 1"; t=4 } @@ -42,7 +42,7 @@ if awk ' sub(/continue 2; fi/, "failed=y") t=1 } - /if \[ -d \"\$TARGET\/sys\" \] && \\/ { sub(/if/, "if [ -z \"$container\" ] \&\&") } + /if \[ -d \"\$TARGET\/sys\" \] && \\/ { sub(/if/, "if false \&\&") } 1 END { if (t != 4) exit 1 } ' "$tmp/functions" > "$tmp/functions.new"; then @@ -67,8 +67,8 @@ if [ ! -f "$tmp/scripts/$RELEASE" ]; then ln -s "$tmp/scripts/$BOOTSTRAP_RELEASE" "$tmp/scripts/$RELEASE" fi -# We are not able to mknod in containers, so make a fake mknod for debootstrap. -if [ -n "$container" ]; then +# Create a fake mknod for debootstrap if we don't have CAP_MKNOD. +if ! capmknod; then mkdir "$tmp/bin" echo "touch \$1" > "$tmp/bin/mknod" chmod +x "$tmp/bin/mknod" diff --git a/installer/ubuntu/prepare b/installer/ubuntu/prepare index b0d197cfb..ceb4e65b5 100644 --- a/installer/ubuntu/prepare +++ b/installer/ubuntu/prepare @@ -219,38 +219,30 @@ export DEBIAN_FRONTEND=noninteractive # Run debootstrap second stage if it hasn't already happened if [ -r /debootstrap ]; then - # Debootstrap doesn't like anything mounted under /sys or /var when it runs + # Debootstrap doesn't like anything mounted under /var when it runs. # We request a re-mount after bootstrapping, so these mounts will be fixed. # We also can't detect the mounts properly due to the chroot, so we have to - # recursively find mountpoints under /sys, and hardcode /var umounts. - # However, if we are in container, debootstrap cannot mount /sys, so don't - # unmount it. - if [ -z "$container" ]; then - find /sys/* -maxdepth 2 -depth -type d \ - -exec mountpoint -q {} \; -exec umount {} \; - else + # hardcode /var umounts. + if mountpoint -q '/var/run/crouton-ext'; then umount '/var/run/crouton-ext' fi umount '/var/run/lock' '/var/run' - # Create a fake mknod if we are in container. - if [ -n "$container" ]; then + # Create a fake mknod for debootstrap if we don't have CAP_MKNOD. + if ! capmknod; then cp /bin/mknod /bin/mknod.old echo "touch \$1" > /bin/mknod fi # Start the bootstrap /debootstrap/debootstrap --second-stage - if [ -n "$container" ]; then - # Mark makedev as installed. + # If we cannot mknod, the installation of makedev will fail, so mark it as + # installed manually. + if ! capmknod; then awk ' BEGIN { matched = 0 } /^Package: makedev$/ { matched = 1 } matched == 1 && /^Status:/ { sub($4, "installed"); matched = 0 } 1' /var/lib/dpkg/status > /var/lib/dpkg/status.new mv -f /var/lib/dpkg/status.new /var/lib/dpkg/status - - # Deny all services to start in container. - echo exit 101 > /usr/sbin/policy-rc.d - chmod +x /usr/sbin/policy-rc.d fi # Clean contents of /var/run (since we'll always be mounting over it). rm -rf --one-file-system /var/run/*