In addition to just umounting filesystems, we also try to tear down volume groups, crypt mappings, and loopback devices. We skip /, /proc, /sys, and /dev when umounting filesystems. We do all of the above in a loop, escalating how forcible we are in umounting filesystems up to the point where we give up and try to just remount filesystems read-only. We do not escalate how forcible we are until we stop making progress in umounting filesystems. This patch does not print anything when tearing down crypt devices, deactivating volume groups, or tearing down loopback devices. It does this because root on lvm and root on dm_crypt are fairly common cases, and figuring out which of these is the case and avoiding that volume group or dm_crypt mapping is tricky. We also no longer rely on /etc/crypttab at all for shutdown to tear down dm_crypt mappings. --- rc.shutdown | 112 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 90 insertions(+), 22 deletions(-) diff --git a/rc.shutdown b/rc.shutdown index 2a561e4..d430dce 100755 --- a/rc.shutdown +++ b/rc.shutdown @@ -64,30 +64,98 @@ stat_busy "Deactivating Swap" /sbin/swapoff -a stat_done -stat_busy "Unmounting Filesystems" -/bin/umount -a -r -t noramfs,notmpfs,nosysfs,noproc,nodevtmpfs -O no_netdev -stat_done +# reverse an array +reverse() { + res=() + [[ $1 ]] || return 0 + local i=("$@") j=0 + for ((j=${#i[@]}-1; j>=0; j--)); do + res+=("${i[j]}") + done +} -# Kill non-root encrypted partition mappings -if [[ -f /etc/crypttab && $CS ]]; then - stat_busy "Deactivating encrypted volumes:" - do_lock() { - stat_append "${1}.." - if $CS remove "$1" >/dev/null 2>&1; then - stat_append "ok " - else - stat_append "failed " - fi - } - read_crypttab do_lock - stat_done -fi +# Get mounted filesystems +fs=() +ignore_re='^/(proc|sys|dev)?$' +while read dev mntpt rest; do + [[ $mntpt =~ $ignore_re ]] && continue + fs+=("$mntpt") +done < /proc/self/mounts +# reverse list of filesystems to let us umount them in reverse order +reverse "${fs[@]}" +fs=("${res[@]}") + +# get volume groups +vg=() +which lvm >/dev/null 2>&1 && \ + vg=($(lvm vgs --unbuffered --noheadings -o vg_name)) +# reverse list of volume groups to grab more of them at once. +reverse "${vg[@]}" +vg=("${res[@]}") + +# get crypto devices +crypt=() +[[ $CS ]] && { + for dev in /dev/mapper/*; do + "$CS" status "${dev##*/}" >/dev/null 2>&1 || continue + crypt+=(${dev##*/}) + done +} +# don't bother reversing crypt, it is in no defined order anyways. + +# get loopback devices in use +loops=() +which losetup >/dev/null 2>&1 && { + while read dev rest; do + loops+=("${dev%:}") + done < <(losetup -a) +} +reverse "${loops[@]}" +loops=("${res[@]}") + +do_umount() { + local ret=0 i j pass=$1 umountopts='' last + # do not umount anything that matches this regex + case $pass in + 2) umountopts='-f';; #next try forcing the umount + 3) umountopts='-r'; last=true;; #next try remounting readonly + esac + + # first, try umounting filesystems. + for i in "${!fs[@]}"; do + if status "Unmounting ${fs[i]} (pass $pass)" /bin/umount -d $umountopts \ + "${fs[i]}"; then + unset fs[$i] + else + ((ret++)) + fi + done + + # Second, deactivate any LVM groups that we can free up + for i in "${!vg[@]}"; do + /sbin/lvm vgchange --sysinit -an "${vg[i]}" >/dev/null 2>&1 && \ + unset vg[$i] + done + + # Third, tear down as many cryptsetup devices as we can. + for i in "${!crypt[@]}"; do + $CS remove "${crypt[i]}" >/dev/null 2>&1 && unset crypt[$i] + done + + # Last, tear down as many loop devices as we can + for i in "${!loops[@]}"; do + losetup -d "${loops[i]}" >/dev/null 2>&1 && unset loops[$i] + done + [[ $last = true ]] && return 0 + return $ret +} -if [[ $USELVM =~ yes|YES && -x /sbin/lvm && -d /sys/block ]]; then - stat_busy "Deactivating LVM2 groups" - /sbin/lvm vgchange --ignorelockingfailure -an >/dev/null 2>&1 - stat_done -fi +# Loop and try to umount filesystems +for ((pass=1,fses=${#fs[@]}; ;fses=${#fs[@]})); do + do_umount $pass && break; # if we umounted all the filesystems, break. + # only escalate if we did not make progress unmounting filesystems. + ((fses == ${#fs[@]})) && ((pass++)) +done stat_busy "Remounting Root Filesystem Read-only" /bin/mount -n -o remount,ro / -- 1.7.1.1