[arch-projects] [mkinitcpio][PATCH 2/2] shutdown: disassemble devices on shutdown

Dave Reisner d at falconindy.com
Sat Mar 24 22:20:05 EDT 2012


sysfs contains enough information about block devices to be able to
determine the order of stacked devices such as lvm, raid, or crypto. By
looking at the device symlinks from the holders/ attributes of a block
device, we can walk down each device chain until we reach the most
descendant device.

For each of these devices at the end of a chain, detect its type and
perform the appropriate action to disassemble it. Then, walk back up the
device chain, disassembling each parent device.

To save ourselves some pain and make sure we're fairly accurate, lsblk
is brought in for detection of device types.

Signed-off-by: Dave Reisner <dreisner at archlinux.org>
---
This was fun... so far, I've tested this with the following root
configurations:

* lvm+crypt+lvm (don't ask)
* raid0
* lvm
* crypt (this setup has encrypted home as well)
* lvm+raid

And it works well for all 5. The idea is that it supports any given number of
stacked devices so we don't need to worry about specifically naming what we're
willing to support.

dmraid isn't tested because i lack the hardware (and im not sure its possible
to test in qemu), but it should "just work".

The only unresolved issue thus far is with the mdadm hook (rather than
mdadm_udev). Since we don't include /sbin/mdadm, there's no way to disassemble
devices via mdassemble. I'm inclined to believe that the lifespan of the mdadm
hook is limited (as upstream really encourages you to use udev assembly these
days), so I think it's okay to just call this setup unsupported.


 install/shutdown |    2 +-
 shutdown         |   94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/install/shutdown b/install/shutdown
index 5b56f17..600209e 100644
--- a/install/shutdown
+++ b/install/shutdown
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 build() {
-    BINARIES='cp'
+    BINARIES='cp lsblk'
     SCRIPT='shutdown'
 
     add_file "/usr/lib/initcpio/shutdown" "/shutdown"
diff --git a/shutdown b/shutdown
index aad0198..6c34ff2 100755
--- a/shutdown
+++ b/shutdown
@@ -1,5 +1,80 @@
 #!/usr/bin/ash
 
+# disassemble a device, $1.
+#   returns 0 on disassembling a device
+#   returns 1 when the nothing is done (caller should stop)
+disassemble_dev() {
+    local devtype=
+
+    [ -n "$1" ] || return 0
+
+    devtype=$(lsblk -drno TYPE "$1")
+    case $devtype in
+        disk|part)
+            return 1
+            ;;
+        crypt)
+            read devname <"${1##*/}/dm/name"
+            cryptsetup luksClose "$devname"
+            return 0
+            ;;
+        dm|lvm)
+            # rip apart a specific VG. it needs to be translated from the LV
+            # that's been passed to this function.
+            read devname <"${1##*/}/dm/name"
+            devname=$(lvm lvs --noheadings -o vg_name "/dev/mapper/$devname")
+
+            # $devname can't be quoted because lvs will put leading and
+            # trailing whitespace in the vgname output. "fortunately",
+            # whitespace is invalid in a vgname, so this is "safe".
+            lvm vgchange -an $devname
+            return 0
+            ;;
+        raid*)
+            mdadm --stop "$1"
+            return 0
+            ;;
+        dmraid)
+            # XXX: i have no idea how dmraid works.
+            dmraid -an
+            return 0
+            ;;
+        *)
+            # unexpected device
+            return 1
+            ;;
+    esac
+}
+
+# get the parent of device $1 from /sys
+#   return 0 on success (and write device to stdout)
+#   return 1 on failure
+get_parent_dev() {
+    local parent=$(printf '%s\n' */holders/${1##*/})
+
+    [ -e "$parent" ] && printf '/dev/%s' "${parent%%/*}"
+}
+
+# get the most descendant device in a device chain
+get_child_dev() {
+  local child= parent=$1
+
+  while :; do
+      for child in "$parent"/holders/*; do
+        if [ -e "$child" ]; then
+            # found a descendant, check for more children below it
+            parent=${child##*/}
+            break
+        else
+            # end of a chain
+            printf '/dev/%s' "$parent"
+            return
+        fi
+      done
+  done
+}
+
+# unmount everything
 findmnt -Rruno TARGET /oldroot | awk '
 BEGIN { i = 0 }
 ! /^\/(proc|dev|sys)/ {
@@ -15,6 +90,25 @@ END {
     umount -l "$mount"
 done
 
+# chdir, so that we can avoid a lot of path chopping
+cd /sys/class/block
+
+# find stacked devices and disassemble them
+for holder in */holders/*; do
+    device=${holder%%/*}
+
+    # walk down the device chain to find the most descendant child
+    nextdev=$(get_child_dev "$device")
+
+    # check to see that the device still exists in /sys. it may have been taken apart already
+    [ -e "${nextdev##*/}" ] || continue
+
+    # now walk back up the chain, disassembling each device
+    while disassemble_dev "$nextdev"; do
+        nextdev=$(get_parent_dev "$nextdev") || break
+    done
+done
+
 case $1 in
     poweroff|shutdown|halt)
         "$1" -f
-- 
1.7.9.4



More information about the arch-projects mailing list